diff --git a/.github/workflows/phpunit-tests.yml b/.github/workflows/phpunit-tests.yml index aa95c42..f7802a9 100644 --- a/.github/workflows/phpunit-tests.yml +++ b/.github/workflows/phpunit-tests.yml @@ -48,39 +48,44 @@ jobs: - 'highest' php: - '8.1' - - '8.2' + - '8.3' elasticsearch: - - '7.17.13' + - '7.17.24' + - '8.15.2' symfony: - - '~5.0' + - '~6.0' include: - - php: '8.1' - symfony: '~5.0' + - php: '8.3' + symfony: '~6.0' elasticsearch: '8.0.1' experimental: false - - php: '8.1' - symfony: '~5.0' + - php: '8.3' + symfony: '~6.0' + elasticsearch: '8.0.1' + experimental: false + - php: '8.3' + symfony: '~6.0' elasticsearch: '8.1.3' # there are some bc in minor version https://www.elastic.co/guide/en/elasticsearch/reference/current/migrating-8.1.html#breaking-changes-8.1 experimental: false - - php: '8.1' - symfony: '~5.0' + - php: '8.3' + symfony: '~6.0' elasticsearch: '8.5.3' # there are some bc in minor version https://www.elastic.co/guide/en/elasticsearch/reference/current/migrating-8.5.html experimental: false - - php: '8.1' - symfony: '~5.0' + - php: '8.3' + symfony: '~6.0' elasticsearch: '8.6.2' # there are no bc in minor version https://www.elastic.co/guide/en/elasticsearch/reference/current/migrating-8.6.html experimental: false - - php: '8.1' - symfony: '~5.0' + - php: '8.3' + symfony: '~6.0' elasticsearch: '8.7.1' # there are no bc in minor version https://www.elastic.co/guide/en/elasticsearch/reference/current/migrating-8.7.html experimental: false - - php: '8.1' - symfony: '~5.0' + - php: '8.3' + symfony: '~6.0' elasticsearch: '8.8.0' # there are no bc in minor version https://www.elastic.co/guide/en/elasticsearch/reference/current/migrating-8.8.html experimental: false - php: '8.1' - symfony: '~6.0' - elasticsearch: '8.10.2' # newest version + symfony: '~5.0' + elasticsearch: '8.15.2' # newest version experimental: false fail-fast: false steps: @@ -115,10 +120,16 @@ jobs: - name: 'Run phpunit tests' run: | - vendor/bin/simple-phpunit --coverage-clover=tests/App/build/clover.xml 2>/dev/null + vendor/bin/simple-phpunit --coverage-clover=tests/App/build/clover.xml - name: Upload coverage results to Coveralls env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} run: | vendor/bin/php-coveralls --coverage_clover=tests/App/build/clover.xml --json_path=tests/App/build/coveralls.json -v + + # Enable tmate debugging on failure for 15 minutes + - name: Setup tmate session + if: ${{ !env.ACT && failure() }} + uses: mxschmitt/action-tmate@v3 + timeout-minutes: 15 diff --git a/.gitignore b/.gitignore index ea85e0a..35c81fc 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ .phpunit.result.cache .idea /.php-cs-fixer.cache +/.phpstan-cache +/.rector-cache/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 3eb4e1f..945768c 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -17,6 +17,7 @@ // Indent '=>' operator 'binary_operator_spaces' => ['operators' => ['=>' => 'align_single_space_minimal']], // PSR12 imports order - 'ordered_imports' => ['imports_order' => ['class', 'function', 'const']], + 'ordered_imports' => ['imports_order' => ['class', 'function', 'const']], + 'phpdoc_separation' => true, ]) ; diff --git a/README.md b/README.md index 25e6367..4ac2699 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ Installation instructions and documentation of the bundle can be found [here](do ## Version matrix | ElasticsearchBundle | Elasticsearch | Symfony | PHP | -| ------------------- | -------------- | ----------- | ----------- | +|---------------------| -------------- |-------------|-------------| +| ~7.2 | >= 7.0 | 5.0+ | 8.1+ | | ~7.0 | >= 7.0 | 4.4+ / 5.0+ | 7.3+ / 8.0+ | | ~6.2 | >= 6.2, < 7.0 | 3.4+ / 4.0+ | 7.3+ | | ~6.1.0 | >= 6.0, < 6.2 | | | diff --git a/composer.json b/composer.json index cf9d7af..6b3e31f 100644 --- a/composer.json +++ b/composer.json @@ -11,37 +11,40 @@ } ], "require": { - "php": "^7.3 || ^8.0", - "psr/log": "^1.0", + "php": "^8.1", + "psr/log": "^2.0 || ^3.0", - "symfony/framework-bundle": "^4.4 || ^5.0", - "symfony/options-resolver": "^4.4 || ^5.0", - "symfony/config": "^4.4 || ^5.0", - "symfony/event-dispatcher": "^4.4 || ^5.0", - "symfony/translation": "^4.4 || ^5.0", - "symfony/console": "^4.4 || ^5.0", - "symfony/http-kernel": "^4.4 || ^5.0", - "symfony/event-dispatcher-contracts": "^1.1 || ^2.2", + "symfony/framework-bundle": "^5.4 || ^6.4", + "symfony/options-resolver": "^5.4 || ^6.4", + "symfony/config": "^5.4 || ^6.4", + "symfony/event-dispatcher": "^5.4 || ^6.4", + "symfony/translation": "^5.4 || ^6.4", + "symfony/console": "^5.4 || ^6.4", + "symfony/http-kernel": "^5.4 || ^6.4", + "symfony/event-dispatcher-contracts": "^3.5", "doctrine/annotations": "^1.2", "doctrine/cache": "^1.4", "elasticsearch/elasticsearch": "^7.0" }, "require-dev": { - "symfony/debug": "^4.4 || ^5.0", - "symfony/stopwatch": "^4.4 || ^5.0", - "symfony/phpunit-bridge": "^4.4 || ^5.0", - "symfony/browser-kit": "^4.4 || ^5.0", - "symfony/dotenv": "^4.4 || ^5.0", + "symfony/stopwatch": "^5.4 || ^6.4", + "symfony/phpunit-bridge": "^5.4 || ^6.4", + "symfony/browser-kit": "^5.4 || ^6.4", + "symfony/dotenv": "^5.4 || ^6.4", "doctrine/orm": "^2.6.3", - "monolog/monolog": "^1.0|^2.0|^3.0", + "monolog/monolog": "^2.0|^3.0", "knplabs/knp-paginator-bundle": "^4.0 || ^5.0", "friendsofphp/php-cs-fixer": "^3.34", "php-coveralls/php-coveralls": "^2.1", "escapestudios/symfony2-coding-standard": "^3.0", "jchook/phpunit-assert-throws": "^1.0", - "dms/phpunit-arraysubset-asserts": "^0.2.1" + "dms/phpunit-arraysubset-asserts": "^0.2.1", + "phpstan/phpstan": "^1.12", + "phpstan/phpstan-symfony": "^1.4", + "phpstan/phpstan-phpunit": "^1.4", + "rector/rector": "^1.2" }, "suggest": { "monolog/monolog": "Allows for client-level logging and tracing", diff --git a/docker-compose.yml b/docker-compose.yml index b7166f8..7957ebc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: elasticsearch: - image: "docker.elastic.co/elasticsearch/elasticsearch:${ELASTICSEARCH_VERSION:-7.17.13}" + image: "docker.elastic.co/elasticsearch/elasticsearch:${ELASTICSEARCH_VERSION:-8.15.2}" container_name: sfes_elasticsearch environment: - discovery.type=single-node diff --git a/phpcs.xml b/phpcs.xml deleted file mode 100644 index aa18289..0000000 --- a/phpcs.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - Custom coding standards for the project - - - - - - - diff --git a/phpstan.dist.neon b/phpstan.dist.neon new file mode 100644 index 0000000..2f8bde4 --- /dev/null +++ b/phpstan.dist.neon @@ -0,0 +1,18 @@ +includes: + - vendor/phpstan/phpstan-symfony/extension.neon + - vendor/phpstan/phpstan-symfony/rules.neon +# - vendor/phpstan/phpstan-phpunit/extension.neon +# - vendor/phpstan/phpstan-phpunit/rules.neon + +parameters: + level: 5 + tmpDir: .phpstan-cache + treatPhpDocTypesAsCertain: false + + symfony: + containerXmlPath: var/cache/test/Sineflow_ElasticsearchBundle_Tests_App_AppKernelTestDebugContainer.xml + + paths: + - config + - src + #- tests diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..3402a2a --- /dev/null +++ b/rector.php @@ -0,0 +1,32 @@ +withCache(__DIR__.'/.rector-cache', FileCacheStorage::class) + ->withSymfonyContainerXml(__DIR__.'/var/cache/test/Sineflow_ElasticsearchBundle_Tests_App_AppKernelTestDebugContainer.xml') + ->withPHPStanConfigs([__DIR__.'/phpstan.dist.neon']) + ->withParallel() + + ->withImportNames(importShortClasses: false) // Allow global classes without use statements + + ->withPaths([ + __DIR__.'/config', + __DIR__.'/src', + __DIR__.'/tests', + ]) + + ->withAttributesSets(symfony: true, doctrine: true) + + ->withSets([ + SymfonySetList::SYMFONY_CODE_QUALITY, + DoctrineSetList::DOCTRINE_CODE_QUALITY, + ]) + + ->withPhpSets() +; diff --git a/src/Annotation/Document.php b/src/Annotation/Document.php index 936946c..5a7fa6c 100644 --- a/src/Annotation/Document.php +++ b/src/Annotation/Document.php @@ -13,28 +13,19 @@ */ final class Document implements DumperInterface { - /** - * @var string - */ - public $repositoryClass; - - /** - * @var string - */ - public $providerClass; + public ?string $repositoryClass = null; + public ?string $providerClass = null; /** * Settings directly passed to Elasticsearch client as-is - * - * @var array */ - public $options; + public array $options = []; /** * {@inheritdoc} */ - public function dump(array $settings = []) + public function dump(array $settings = []): array { - return (array) $this->options; + return $this->options; } } diff --git a/src/Annotation/Id.php b/src/Annotation/Id.php index 8f3fa30..d08d5ae 100644 --- a/src/Annotation/Id.php +++ b/src/Annotation/Id.php @@ -9,6 +9,18 @@ * * @Target("PROPERTY") */ -final class Id +final class Id implements PropertyAnnotationInterface { + public const NAME = '_id'; + public const TYPE = 'keyword'; + + public function getName(): ?string + { + return self::NAME; + } + + public function getType(): ?string + { + return self::TYPE; + } } diff --git a/src/Annotation/Property.php b/src/Annotation/Property.php index e05d0e2..7342100 100644 --- a/src/Annotation/Property.php +++ b/src/Annotation/Property.php @@ -5,74 +5,67 @@ use Sineflow\ElasticsearchBundle\Mapping\DumperInterface; /** - * Annotation used to check mapping type during the parsing process. + * Annotation used for all properties of the document, apart from the special ones (like _id and _score) * * @Annotation * * @Target("PROPERTY") */ -final class Property implements DumperInterface +final class Property implements PropertyAnnotationInterface, DumperInterface { public const LANGUAGE_PLACEHOLDER = '{lang}'; public const DEFAULT_LANG_SUFFIX = 'default'; /** - * @var string - * * @Required */ - public $name; + public string $name; /** - * @var string - * * @Required */ - public $type; + public string $type; - /** - * @var bool - */ - public $multilanguage; + public bool $multilanguage = false; /** * Override mapping for the 'default' language field of multilanguage properties - * - * @var array */ - public $multilanguageDefaultOptions; + public array $multilanguageDefaultOptions = []; /** * The object name must be defined, if type is 'object' or 'nested' - * - * @var string Object name to map. */ - public $objectName; + public string $objectName; /** * Defines if related object will have one or multiple values. * If this value is set to true, ObjectIterator will be provided in the result, as opposed to an ObjectInterface object - * - * @var bool */ - public $multiple; + public bool $multiple = false; /** * Settings directly passed to Elasticsearch client as-is - * - * @var array */ - public $options; + public array $options = []; + + public function getName(): ?string + { + return $this->name; + } + + public function getType(): ?string + { + return $this->type; + } /** * Dumps property fields as array for index mapping - * - * @return array */ - public function dump(array $settings = []) + public function dump(array $settings = []): array { - $result = (array) $this->options; + $result = $this->options; // Although it is completely valid syntax to explicitly define objects as such in the mapping definition, ES does not do that by default. // So, in order to ensure that the mapping for index creation would exactly match the mapping returned from the ES _mapping endpoint, we don't explicitly set 'object' data types @@ -86,7 +79,7 @@ public function dump(array $settings = []) } // Recursively replace {lang} in any string option with the respective language - \array_walk_recursive($result, static function (&$value, $key, $settings) { + \array_walk_recursive($result, static function (&$value, $key, $settings): void { if (\is_string($value) && \str_contains($value, self::LANGUAGE_PLACEHOLDER)) { if (\in_array($key, ['analyzer', 'index_analyzer', 'search_analyzer'])) { // Replace {lang} in any analyzers with the respective language diff --git a/src/Annotation/PropertyAnnotationInterface.php b/src/Annotation/PropertyAnnotationInterface.php new file mode 100644 index 0000000..b3fc50c --- /dev/null +++ b/src/Annotation/PropertyAnnotationInterface.php @@ -0,0 +1,10 @@ +metadataCollector = $metadataCollector; } - /** - * @return bool - */ - public function isOptional() + public function isOptional(): bool { return true; } - /** - * @param string $cacheDir - * - * @return array - * - * @throws \Psr\Cache\InvalidArgumentException - */ - public function warmUp($cacheDir) + public function warmUp(string $cacheDir): array { - if ($this->metadataCollector instanceof WarmableInterface) { - return (array) $this->metadataCollector->warmUp($cacheDir); - } - - throw new \LogicException(\sprintf('The metadata collector "%s" cannot be warmed up because it does not implement "%s".', \get_debug_type($this->metadataCollector), WarmableInterface::class)); + return $this->metadataCollector->warmUp($cacheDir); } } diff --git a/src/Command/IndexBuildCommand.php b/src/Command/IndexBuildCommand.php index fd2fa87..dac01c4 100644 --- a/src/Command/IndexBuildCommand.php +++ b/src/Command/IndexBuildCommand.php @@ -16,22 +16,15 @@ class IndexBuildCommand extends Command { protected static $defaultName = 'sineflow:es:index:build'; - /** - * @var IndexManagerRegistry - */ - private $indexManagerRegistry; - - public function __construct(IndexManagerRegistry $indexManagerRegistry) + public function __construct(private readonly IndexManagerRegistry $indexManagerRegistry) { - $this->indexManagerRegistry = $indexManagerRegistry; - parent::__construct(); } /** * {@inheritdoc} */ - protected function configure() + protected function configure(): void { parent::configure(); @@ -59,7 +52,7 @@ protected function configure() /** * {@inheritdoc} */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $indexManagerName = $input->getArgument('index'); $indexManager = $this->indexManagerRegistry->get($indexManagerName); diff --git a/src/Command/IndexCreateCommand.php b/src/Command/IndexCreateCommand.php index 55b36aa..aadec7e 100644 --- a/src/Command/IndexCreateCommand.php +++ b/src/Command/IndexCreateCommand.php @@ -15,22 +15,15 @@ class IndexCreateCommand extends Command { protected static $defaultName = 'sineflow:es:index:create'; - /** - * @var IndexManagerRegistry - */ - private $indexManagerRegistry; - - public function __construct(IndexManagerRegistry $indexManagerRegistry) + public function __construct(private readonly IndexManagerRegistry $indexManagerRegistry) { - $this->indexManagerRegistry = $indexManagerRegistry; - parent::__construct(); } /** * {@inheritdoc} */ - protected function configure() + protected function configure(): void { parent::configure(); @@ -46,7 +39,7 @@ protected function configure() /** * {@inheritdoc} */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $indexManagerName = $input->getArgument('index'); $indexManager = $this->indexManagerRegistry->get($indexManagerName); diff --git a/src/DTO/BulkQueryItem.php b/src/DTO/BulkQueryItem.php index 2762990..f5ba30d 100644 --- a/src/DTO/BulkQueryItem.php +++ b/src/DTO/BulkQueryItem.php @@ -9,41 +9,25 @@ */ class BulkQueryItem { - /** - * @var string - */ - private $operation; - - /** - * @var string - */ - private $index; - - /** - * @var array - */ - private $query; - - /** - * @var array - */ - private $metaParams; + private readonly array $query; + private readonly array $metaParams; /** * @param string $operation One of: index, update, delete, create. * @param string $index Elasticsearch index name. * @param array $query Bulk item query (aka optional_source in the ES docs) - * @param array $metaParams Additional params to pass with the meta data in the bulk request (_version, _routing, etc.) + * @param array $metaParams Additional params to pass with the metadata in the bulk request (_version, _routing, etc.) */ - public function __construct(string $operation, string $index, array $query, array $metaParams = []) - { - if (!\in_array($operation, ['index', 'create', 'update', 'delete'])) { + public function __construct( + private readonly string $operation, + private readonly string $index, + array $query, + array $metaParams = [], + ) { + if (!\in_array($this->operation, ['index', 'create', 'update', 'delete'])) { throw new InvalidArgumentException(\sprintf('Invalid bulk operation "%s" specified', $operation)); } - $this->operation = $operation; - $this->index = $index; - // in case some meta param is specified as part of the query and not in $metaParams, move it there // (this happens when converting a document entity to an array) foreach (['_id'] as $metaParam) { @@ -72,7 +56,7 @@ public function getQuery(): array * * @param string|null $forceIndex If set, that will be the index used for the output bulk request */ - public function getLines($forceIndex = null): array + public function getLines(?string $forceIndex = null): array { $result = []; diff --git a/src/DTO/IndicesToDocumentClasses.php b/src/DTO/IndicesToDocumentClasses.php index dbdd00b..cd86bcf 100644 --- a/src/DTO/IndicesToDocumentClasses.php +++ b/src/DTO/IndicesToDocumentClasses.php @@ -9,10 +9,8 @@ class IndicesToDocumentClasses { /** * => - * - * @var array */ - private $documentClasses = []; + private array $documentClasses = []; /** * Set the document class for the physical index name @@ -21,7 +19,7 @@ class IndicesToDocumentClasses * `null` to be passed if there's only one index and we don't need the actual index name * @param string $documentClass The document class in short notation */ - public function set(?string $index, string $documentClass) + public function set(?string $index, string $documentClass): void { if (!$index) { if (!empty($this->documentClasses)) { @@ -40,10 +38,8 @@ public function set(?string $index, string $documentClass) * Get the document class for the physical index name * * @param string $index The name of the physical index in Elasticsearch - * - * @return string */ - public function get($index) + public function get(string $index): string { if (isset($this->documentClasses[$index])) { return $this->documentClasses[$index]; diff --git a/src/DependencyInjection/Compiler/AddConnectionsPass.php b/src/DependencyInjection/Compiler/AddConnectionsPass.php index 2be5b50..accd4a4 100644 --- a/src/DependencyInjection/Compiler/AddConnectionsPass.php +++ b/src/DependencyInjection/Compiler/AddConnectionsPass.php @@ -16,7 +16,7 @@ class AddConnectionsPass implements CompilerPassInterface /** * {@inheritdoc} */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $connections = $container->getParameter('sfes.connections'); diff --git a/src/DependencyInjection/Compiler/AddIndexManagersPass.php b/src/DependencyInjection/Compiler/AddIndexManagersPass.php index 7c4e482..f470075 100644 --- a/src/DependencyInjection/Compiler/AddIndexManagersPass.php +++ b/src/DependencyInjection/Compiler/AddIndexManagersPass.php @@ -17,7 +17,7 @@ class AddIndexManagersPass implements CompilerPassInterface /** * {@inheritdoc} */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $indices = $container->getParameter('sfes.indices'); diff --git a/src/DependencyInjection/Compiler/SetCachePass.php b/src/DependencyInjection/Compiler/SetCachePass.php index 6cac8d5..6e50114 100644 --- a/src/DependencyInjection/Compiler/SetCachePass.php +++ b/src/DependencyInjection/Compiler/SetCachePass.php @@ -15,7 +15,7 @@ class SetCachePass implements CompilerPassInterface /** * {@inheritdoc} */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $customCachePool = $container->resolveEnvPlaceholders($container->getParameter('sfes.cache_pool'), true); diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index b624650..cf7a457 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -15,7 +15,7 @@ class Configuration implements ConfigurationInterface /** * {@inheritdoc} */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('sineflow_elasticsearch'); $rootNode = $treeBuilder->getRootNode(); diff --git a/src/DependencyInjection/SineflowElasticsearchExtension.php b/src/DependencyInjection/SineflowElasticsearchExtension.php index 2a791f9..a0c083a 100644 --- a/src/DependencyInjection/SineflowElasticsearchExtension.php +++ b/src/DependencyInjection/SineflowElasticsearchExtension.php @@ -6,7 +6,7 @@ use Sineflow\ElasticsearchBundle\Document\Repository\ServiceRepositoryInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; /** @@ -14,10 +14,7 @@ */ class SineflowElasticsearchExtension extends Extension { - /** - * @return Configuration - */ - public function getConfiguration(array $config, ContainerBuilder $container) + public function getConfiguration(array $config, ContainerBuilder $container): Configuration { return new Configuration(); } @@ -25,12 +22,12 @@ public function getConfiguration(array $config, ContainerBuilder $container) /** * {@inheritdoc} */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); - $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../../config')); + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../../config')); $loader->load('services.yml'); $container->setParameter('sfes.entity_locations', $config['entity_locations']); diff --git a/src/Document/AbstractDocument.php b/src/Document/AbstractDocument.php index 981012d..fc8ad44 100644 --- a/src/Document/AbstractDocument.php +++ b/src/Document/AbstractDocument.php @@ -10,16 +10,12 @@ abstract class AbstractDocument implements DocumentInterface { /** - * @var string - * * @ES\Id */ - public $id; + public string|int|null $id = null; /** - * @var float - * * @ES\Score */ - public $score; + public ?float $score = null; } diff --git a/src/Document/MLProperty.php b/src/Document/MLProperty.php index bbdeaa3..41ae6ba 100644 --- a/src/Document/MLProperty.php +++ b/src/Document/MLProperty.php @@ -14,7 +14,7 @@ class MLProperty /** * @var string[] */ - private $values = []; + private array $values = []; public function __construct(array $values = []) { @@ -25,11 +25,8 @@ public function __construct(array $values = []) /** * Set value of property in given language - * - * @param string $value - * @param string $language */ - public function setValue($value, $language) + public function setValue(?string $value, string $language): void { $this->values[$language] = $value; } @@ -37,12 +34,9 @@ public function setValue($value, $language) /** * Gets value based on passed language, falling back on the default language, by default * - * @param string $language - * @param bool $fallbackToDefault If set and value for the requested language is missing, return default language value - * - * @return string|null + * @param bool $fallbackToDefault If set and value for the requested language is missing, return default language value */ - public function getValue($language, $fallbackToDefault = true) + public function getValue(string $language, bool $fallbackToDefault = true): ?string { if (isset($this->values[$language])) { return $this->values[$language]; @@ -56,7 +50,7 @@ public function getValue($language, $fallbackToDefault = true) /** * @return string[] */ - public function getValues() + public function getValues(): array { return $this->values; } diff --git a/src/Document/Provider/AbstractDoctrineProvider.php b/src/Document/Provider/AbstractDoctrineProvider.php index fe98d47..e2675f1 100644 --- a/src/Document/Provider/AbstractDoctrineProvider.php +++ b/src/Document/Provider/AbstractDoctrineProvider.php @@ -12,42 +12,34 @@ */ abstract class AbstractDoctrineProvider extends AbstractProvider { - /** - * @var EntityManagerInterface - */ - protected $em; - - /** - * @var Query - */ - protected $query; + protected Query $query; /** - * @var int How many records to retrieve from DB at once + * How many records to retrieve from DB at once */ - protected $batchSize = 1000; + protected int $batchSize = 1000; /** - * @var string The Doctrine entity name + * The Doctrine entity name */ - protected $doctrineEntityName; + protected string $doctrineEntityName; /** - * @var int How to hydrate doctrine results + * How to hydrate doctrine results + * + * @phpstan-param AbstractQuery::HYDRATE_* $sourceDataHydration */ - protected $sourceDataHydration = AbstractQuery::HYDRATE_OBJECT; + protected int|string $sourceDataHydration = AbstractQuery::HYDRATE_OBJECT; /** * @param string $documentClass The document class the provider is for * @param EntityManagerInterface $em The Doctrine entity manager */ - public function __construct(string $documentClass, EntityManagerInterface $em) + public function __construct(protected string $documentClass, protected EntityManagerInterface $em) { - parent::__construct($documentClass); - $this->em = $em; } - public function setBatchSize(int $batchSize) + public function setBatchSize(int $batchSize): void { $this->batchSize = $batchSize; } @@ -60,21 +52,19 @@ abstract public function getQuery(): Query; /** * Converts a Doctrine entity to Elasticsearch entity * - * @param mixed $entity A doctrine entity object or data array + * @param object|array $entity A doctrine entity object or data array * - * @return mixed An ES document entity object or document array + * @return DocumentInterface|array An ES document entity object or document array */ - abstract protected function getAsDocument($entity); + abstract protected function getAsDocument(object|array $entity): DocumentInterface|array; /** * Returns a PHP Generator for iterating over the full dataset of source data that is to be inserted in ES * The returned data can be either a document entity or an array ready for direct sending to ES * * @return \Generator - * - * @throws \Doctrine\Common\Persistence\Mapping\MappingException */ - public function getDocuments() + public function getDocuments(): \Generator { \set_time_limit(3600); @@ -105,12 +95,8 @@ public function getDocuments() /** * Build and return a document entity from the data source * The returned data can be either a document entity or an array ready for direct sending to ES - * - * @param int|string $id - * - * @return DocumentInterface|array */ - public function getDocument($id) + public function getDocument(int|string $id): DocumentInterface|array|null { $entity = $this->em->getRepository($this->doctrineEntityName)->find($id); diff --git a/src/Document/Provider/AbstractProvider.php b/src/Document/Provider/AbstractProvider.php index a1e4cc6..3a3eb4b 100644 --- a/src/Document/Provider/AbstractProvider.php +++ b/src/Document/Provider/AbstractProvider.php @@ -15,17 +15,13 @@ abstract class AbstractProvider implements ProviderInterface * * @return \Generator */ - abstract public function getDocuments(); + abstract public function getDocuments(): \Generator; /** * Build and return a document entity from the data source * The returned data can be either a document entity or an array ready for direct sending to ES - * - * @param int|string $id - * - * @return DocumentInterface|array */ - abstract public function getDocument($id); + abstract public function getDocument(int|string $id): DocumentInterface|array|null; /** * Returns the number of Elasticsearch documents to persist in a single bulk request diff --git a/src/Document/Provider/ElasticsearchProvider.php b/src/Document/Provider/ElasticsearchProvider.php index ad0389c..3a9612f 100644 --- a/src/Document/Provider/ElasticsearchProvider.php +++ b/src/Document/Provider/ElasticsearchProvider.php @@ -13,43 +13,25 @@ class ElasticsearchProvider extends AbstractProvider { /** - * @var DocumentMetadataCollector + * Specify how long a consistent view of the index should be maintained for a scrolled search */ - protected $metadataCollector; + protected string $scrollTime = '5m'; /** - * @var string The index manager of the data source + * Number of documents in one chunk sent to ES */ - protected $sourceIndexManager; - - /** - * @var string The document class the data is coming from - */ - protected $sourceDocumentClass; - - /** - * @var string Specify how long a consistent view of the index should be maintained for a scrolled search - */ - protected $scrollTime = '5m'; - - /** - * @var int Number of documents in one chunk sent to ES - */ - protected $chunkSize = 500; + protected int $chunkSize = 500; /** * @param DocumentMetadataCollector $metadataCollector The metadata collector - * @param IndexManager $sourceIndexManager The index manager of the data source + * @param string|IndexManager $sourceIndexManager The index manager of the data source * @param string $sourceDocumentClass The document class the data is coming from */ public function __construct( - DocumentMetadataCollector $metadataCollector, - IndexManager $sourceIndexManager, - string $sourceDocumentClass + protected DocumentMetadataCollector $metadataCollector, + protected string|IndexManager $sourceIndexManager, + protected string $sourceDocumentClass, ) { - $this->metadataCollector = $metadataCollector; - $this->sourceIndexManager = $sourceIndexManager; - $this->sourceDocumentClass = $sourceDocumentClass; } /** @@ -57,7 +39,7 @@ public function __construct( * * @return \Generator */ - public function getDocuments() + public function getDocuments(): \Generator { $repo = $this->sourceIndexManager->getRepository(); @@ -83,10 +65,8 @@ public function getDocuments() /** * Build and return a document from the data source, ready for insertion into ES - * - * @param int|string $id */ - public function getDocument($id): array + public function getDocument(int|string $id): array { $params = [ 'index' => $this->sourceIndexManager->getLiveIndex(), diff --git a/src/Document/Provider/ProviderInterface.php b/src/Document/Provider/ProviderInterface.php index 23ccf79..697335b 100644 --- a/src/Document/Provider/ProviderInterface.php +++ b/src/Document/Provider/ProviderInterface.php @@ -16,17 +16,13 @@ interface ProviderInterface * * @return \Generator */ - public function getDocuments(); + public function getDocuments(): \Generator; /** * Build and return a document entity from the data source * The returned data can be either a document entity or an array ready for direct sending to ES - * - * @param int|string $id - * - * @return DocumentInterface|array */ - public function getDocument($id); + public function getDocument(int|string $id): DocumentInterface|array|null; /** * Returns the number of Elasticsearch documents to persist in a single bulk request diff --git a/src/Document/Provider/ProviderRegistry.php b/src/Document/Provider/ProviderRegistry.php index 18f2f04..985e08c 100644 --- a/src/Document/Provider/ProviderRegistry.php +++ b/src/Document/Provider/ProviderRegistry.php @@ -2,6 +2,7 @@ namespace Sineflow\ElasticsearchBundle\Document\Provider; +use Psr\Cache\InvalidArgumentException; use Sineflow\ElasticsearchBundle\Manager\IndexManagerRegistry; use Sineflow\ElasticsearchBundle\Mapping\DocumentMetadataCollector; use Symfony\Component\DependencyInjection\ServiceLocator; @@ -11,41 +12,17 @@ */ class ProviderRegistry { - /** - * @var DocumentMetadataCollector - */ - private $documentMetadataCollector; - - /** - * @var IndexManagerRegistry - */ - private $indexManagerRegistry; - - /** - * @var string - */ - private $selfProviderClass; - - /** - * @var ServiceLocator - */ - private $serviceLocator; - - /** - * ProviderRegistry constructor. - */ public function __construct( - ServiceLocator $serviceLocator, - DocumentMetadataCollector $documentMetadataCollector, - IndexManagerRegistry $indexManagerRegistry, - string $selfProviderClass + private readonly ServiceLocator $serviceLocator, + private readonly DocumentMetadataCollector $documentMetadataCollector, + private readonly IndexManagerRegistry $indexManagerRegistry, + private readonly string $selfProviderClass, ) { - $this->serviceLocator = $serviceLocator; - $this->documentMetadataCollector = $documentMetadataCollector; - $this->indexManagerRegistry = $indexManagerRegistry; - $this->selfProviderClass = $selfProviderClass; } + /** + * @throws InvalidArgumentException + */ public function getCustomProviderForEntity(string $documentClass): ?ProviderInterface { $documentMetadata = $this->documentMetadataCollector->getDocumentMetadata($documentClass); diff --git a/src/Document/Repository/Repository.php b/src/Document/Repository/Repository.php index 93e7bca..0b33e24 100644 --- a/src/Document/Repository/Repository.php +++ b/src/Document/Repository/Repository.php @@ -2,39 +2,30 @@ namespace Sineflow\ElasticsearchBundle\Document\Repository; +use Psr\Cache\InvalidArgumentException; +use Sineflow\ElasticsearchBundle\Document\DocumentInterface; +use Sineflow\ElasticsearchBundle\Finder\Adapter\KnpPaginatorAdapter; +use Sineflow\ElasticsearchBundle\Finder\Adapter\ScrollAdapter; use Sineflow\ElasticsearchBundle\Finder\Finder; use Sineflow\ElasticsearchBundle\Manager\IndexManager; +use Sineflow\ElasticsearchBundle\Result\DocumentIterator; /** * Base entity repository class. */ class Repository { - /** - * @var IndexManager - */ - private $indexManager; - /** * The document class FQN or in short notation (e.g. App:Product) - * - * @var string */ - protected $documentClass; + protected string $documentClass; /** - * @var Finder + * @throws InvalidArgumentException */ - protected $finder; - - /** - * Constructor. - */ - public function __construct(IndexManager $indexManager, Finder $finder) + public function __construct(private readonly IndexManager $indexManager, protected Finder $finder) { - $this->indexManager = $indexManager; - $this->finder = $finder; - $this->documentClass = $indexManager->getDocumentClass(); + $this->documentClass = $this->indexManager->getDocumentClass(); } public function getIndexManager(): IndexManager @@ -45,12 +36,9 @@ public function getIndexManager(): IndexManager /** * Returns a single document data by ID or null if document is not found. * - * @param string $id Document Id to find. - * @param int $resultType Result type returned. - * - * @return mixed + * @throws InvalidArgumentException */ - public function getById(string $id, int $resultType = Finder::RESULTS_OBJECT) + public function getById(string|int $id, int $resultType = Finder::RESULTS_OBJECT): DocumentInterface|array|null { return $this->finder->get($this->documentClass, $id, $resultType); } @@ -62,10 +50,8 @@ public function getById(string $id, int $resultType = Finder::RESULTS_OBJECT) * @param int $resultsType Bitmask value determining how the results are returned * @param array $additionalRequestParams Additional params to pass to the ES client's search() method * @param int|null $totalHits The total hits of the query response - * - * @return mixed */ - public function find(array $searchBody, int $resultsType = Finder::RESULTS_OBJECT, array $additionalRequestParams = [], int &$totalHits = null) + public function find(array $searchBody, int $resultsType = Finder::RESULTS_OBJECT, array $additionalRequestParams = [], ?int &$totalHits = null): array|KnpPaginatorAdapter|ScrollAdapter|DocumentIterator { return $this->finder->find([$this->documentClass], $searchBody, $resultsType, $additionalRequestParams, $totalHits); } diff --git a/src/Document/Repository/RepositoryFactory.php b/src/Document/Repository/RepositoryFactory.php index afa91cb..f732167 100644 --- a/src/Document/Repository/RepositoryFactory.php +++ b/src/Document/Repository/RepositoryFactory.php @@ -2,6 +2,7 @@ namespace Sineflow\ElasticsearchBundle\Document\Repository; +use Psr\Cache\InvalidArgumentException; use Sineflow\ElasticsearchBundle\Finder\Finder; use Sineflow\ElasticsearchBundle\Manager\IndexManager; use Sineflow\ElasticsearchBundle\Mapping\DocumentMetadata; @@ -12,30 +13,18 @@ */ class RepositoryFactory { - /** - * @var ServiceLocator - */ - private $container; - - /** - * @var Finder - */ - private $finder; - /** * @var Repository[] */ - private $knownRepositories = []; + private array $knownRepositories = []; - /** - * RepositoryFactory constructor. - */ - public function __construct(ServiceLocator $container, Finder $finder) + public function __construct(private readonly ServiceLocator $container, private readonly Finder $finder) { - $this->container = $container; - $this->finder = $finder; } + /** + * @throws InvalidArgumentException + */ public function getRepository(IndexManager $indexManager): Repository { $documentMetadata = $indexManager->getDocumentMetadata(); @@ -68,19 +57,15 @@ public function getRepository(IndexManager $indexManager): Repository // if we already have the repository instance for this IndexManager from before, return it straight away $repositoryHash = $documentMetadata->getClassName().\spl_object_hash($indexManager); - if (isset($this->knownRepositories[$repositoryHash])) { - return $this->knownRepositories[$repositoryHash]; - } // create a new Repository instance - return $this->knownRepositories[$repositoryHash] = $this->createRepository($indexManager, $documentMetadata); + return $this->knownRepositories[$repositoryHash] ?? ($this->knownRepositories[$repositoryHash] = $this->createRepository($indexManager, $documentMetadata)); } private function createRepository(IndexManager $indexManager, DocumentMetadata $documentMetadata): Repository { $repositoryClass = $documentMetadata->getRepositoryClass() ?: Repository::class; - $repository = new $repositoryClass($indexManager, $this->finder); - return $repository; + return new $repositoryClass($indexManager, $this->finder); } } diff --git a/src/Event/PostCommitEvent.php b/src/Event/PostCommitEvent.php index e97963a..68b1de9 100644 --- a/src/Event/PostCommitEvent.php +++ b/src/Event/PostCommitEvent.php @@ -10,34 +10,19 @@ */ class PostCommitEvent extends Event { - /** - * @var array - */ - private $bulkResponse; + private readonly string $connectionName; - /** - * @var string - */ - private $connectionName; - - public function __construct(array $bulkResponse, ConnectionManager $connectionManager) + public function __construct(private readonly array $bulkResponse, ConnectionManager $connectionManager) { - $this->bulkResponse = $bulkResponse; $this->connectionName = $connectionManager->getConnectionName(); } - /** - * @return array - */ - public function getBulkResponse() + public function getBulkResponse(): array { return $this->bulkResponse; } - /** - * @return string - */ - public function getConnectionName() + public function getConnectionName(): string { return $this->connectionName; } diff --git a/src/Event/PrePersistEvent.php b/src/Event/PrePersistEvent.php index c368152..2189c9a 100644 --- a/src/Event/PrePersistEvent.php +++ b/src/Event/PrePersistEvent.php @@ -6,53 +6,28 @@ use Sineflow\ElasticsearchBundle\Manager\ConnectionManager; use Symfony\Contracts\EventDispatcher\Event; -/** - * Class PrePersistEvent - */ class PrePersistEvent extends Event { - /** - * @var DocumentInterface - */ - private $document; + private readonly string $connectionName; + private readonly int $bulkOperationIndex; - /** - * @var string - */ - private $connectionName; - - /** - * @var int - */ - private $bulkOperationIndex; - - public function __construct(DocumentInterface $document, ConnectionManager $connectionManager) + public function __construct(private readonly DocumentInterface $document, ConnectionManager $connectionManager) { - $this->document = $document; $this->connectionName = $connectionManager->getConnectionName(); $this->bulkOperationIndex = $connectionManager->getBulkOperationsCount(); } - /** - * @return DocumentInterface - */ - public function getDocument() + public function getDocument(): DocumentInterface { return $this->document; } - /** - * @return string - */ - public function getConnectionName() + public function getConnectionName(): string { return $this->connectionName; } - /** - * @return int - */ - public function getBulkOperationIndex() + public function getBulkOperationIndex(): int { return $this->bulkOperationIndex; } diff --git a/src/Exception/BulkRequestException.php b/src/Exception/BulkRequestException.php index e291807..0a630fe 100644 --- a/src/Exception/BulkRequestException.php +++ b/src/Exception/BulkRequestException.php @@ -7,31 +7,21 @@ */ class BulkRequestException extends Exception { - private $bulkResponseItems = []; + private array $bulkResponseItems = []; + private array $bulkRequest = []; - private $bulkRequest = []; - - /** - * @param string $bulkResponseItems - */ - public function setBulkResponseItems($bulkResponseItems, array $bulkRequest) + public function setBulkResponseItems(array $bulkResponseItems, array $bulkRequest): void { $this->bulkResponseItems = $bulkResponseItems; $this->bulkRequest = $bulkRequest; } - /** - * @return array - */ - public function getBulkResponseItems() + public function getBulkResponseItems(): array { return $this->bulkResponseItems; } - /** - * @return array - */ - public function getBulkRequest() + public function getBulkRequest(): array { return $this->bulkRequest; } diff --git a/src/Exception/DocumentConversionException.php b/src/Exception/DocumentConversionException.php index d9542db..82e63ea 100644 --- a/src/Exception/DocumentConversionException.php +++ b/src/Exception/DocumentConversionException.php @@ -2,9 +2,6 @@ namespace Sineflow\ElasticsearchBundle\Exception; -/** - * Class DocumentConversionException - */ class DocumentConversionException extends Exception { } diff --git a/src/Exception/IndexOrAliasNotFoundException.php b/src/Exception/IndexOrAliasNotFoundException.php index 0a2796d..473a59f 100644 --- a/src/Exception/IndexOrAliasNotFoundException.php +++ b/src/Exception/IndexOrAliasNotFoundException.php @@ -7,21 +7,13 @@ */ class IndexOrAliasNotFoundException extends Exception { - /** - * @var string - */ - private $indexOrAlias; - - /** - * Constructor - * - * @param int $code - */ - public function __construct(string $indexOrAlias, bool $isAlias = false, $code = 0, \Throwable $previous = null) - { - $this->indexOrAlias = $indexOrAlias; - - parent::__construct(\sprintf('%s "%s" does not exist', $isAlias ? 'Alias' : 'Index', $indexOrAlias), $code, $previous); + public function __construct( + private readonly string $indexOrAlias, + bool $isAlias = false, + int $code = 0, + ?\Throwable $previous = null, + ) { + parent::__construct(\sprintf('%s "%s" does not exist', $isAlias ? 'Alias' : 'Index', $this->indexOrAlias), $code, $previous); } public function getIndexOrAlias(): string diff --git a/src/Exception/IndexRebuildingException.php b/src/Exception/IndexRebuildingException.php index c44b130..86c3cf5 100644 --- a/src/Exception/IndexRebuildingException.php +++ b/src/Exception/IndexRebuildingException.php @@ -7,26 +7,19 @@ */ class IndexRebuildingException extends Exception { - /** - * @var array - */ - private $indicesInProgress; + private readonly array $indicesInProgress; /** * @param array $indicesInProgress The physical indices, which are in the process of being built - * @param int $code */ - public function __construct(array $indicesInProgress, $code = 0, Exception $previous = null) + public function __construct(array $indicesInProgress, int $code = 0, ?Exception $previous = null) { parent::__construct(\sprintf('Index is currently being rebuilt as "%s"', \implode(', ', $indicesInProgress)), $code, $previous); $this->indicesInProgress = $indicesInProgress; } - /** - * @return array - */ - public function getIndices() + public function getIndices(): array { return $this->indicesInProgress; } diff --git a/src/Exception/InvalidIndexManagerException.php b/src/Exception/InvalidIndexManagerException.php index e51685d..d69728f 100644 --- a/src/Exception/InvalidIndexManagerException.php +++ b/src/Exception/InvalidIndexManagerException.php @@ -2,9 +2,6 @@ namespace Sineflow\ElasticsearchBundle\Exception; -/** - * Class InvalidIndexManagerException - */ class InvalidIndexManagerException extends Exception { } diff --git a/src/Finder/Adapter/KnpPaginatorAdapter.php b/src/Finder/Adapter/KnpPaginatorAdapter.php index d8eda4a..e9bb1f8 100644 --- a/src/Finder/Adapter/KnpPaginatorAdapter.php +++ b/src/Finder/Adapter/KnpPaginatorAdapter.php @@ -3,74 +3,33 @@ namespace Sineflow\ElasticsearchBundle\Finder\Adapter; use Sineflow\ElasticsearchBundle\Finder\Finder; +use Sineflow\ElasticsearchBundle\Result\DocumentIterator; -/** - * Class KnpPaginatorAdapter - */ class KnpPaginatorAdapter { - /** - * @var Finder - */ - private $finder; - - /** - * @var array - */ - private $documentClasses; - - /** - * @var array - */ - private $searchBody; - - /** - * @var int - */ - private $resultsType; - - /** - * @var array - */ - private $additionalRequestParams; - - /** - * @var int - */ - private $totalHits; - - /** - * @param int $resultsType - */ - public function __construct(Finder $finder, array $documentClasses, array $searchBody, $resultsType, array $additionalRequestParams = []) - { - $this->finder = $finder; - $this->documentClasses = $documentClasses; - $this->searchBody = $searchBody; + private readonly int $resultsType; + private int $totalHits = 0; + + public function __construct( + private readonly Finder $finder, + private readonly array $documentClasses, + private readonly array $searchBody, + int $resultsType, + private readonly array $additionalRequestParams = [], + ) { // Make sure we don't get an adapter returned again when we recursively execute the paginated find() $this->resultsType = $resultsType & ~Finder::BITMASK_RESULT_ADAPTERS; - $this->additionalRequestParams = $additionalRequestParams; } - /** - * @return int - */ - public function getResultsType() + public function getResultsType(): int { return $this->resultsType; } /** * Return results for this page only - * - * @param int $offset - * @param int $count - * @param string $sortField - * @param string $sortDir - * - * @return mixed */ - public function getResults($offset, $count, $sortField = null, $sortDir = 'asc') + public function getResults(int $offset, int $count, ?string $sortField = null, string $sortDir = 'asc'): array|DocumentIterator { $searchBody = $this->searchBody; $searchBody['from'] = $offset; @@ -93,10 +52,8 @@ public function getResults($offset, $count, $sortField = null, $sortDir = 'asc') /** * Return the total hits from the executed getResults() - * - * @return int */ - public function getTotalHits() + public function getTotalHits(): int { return $this->totalHits; } diff --git a/src/Finder/Adapter/ScrollAdapter.php b/src/Finder/Adapter/ScrollAdapter.php index 69e9cf3..d51dbc3 100644 --- a/src/Finder/Adapter/ScrollAdapter.php +++ b/src/Finder/Adapter/ScrollAdapter.php @@ -4,61 +4,35 @@ use Sineflow\ElasticsearchBundle\Exception\Exception; use Sineflow\ElasticsearchBundle\Finder\Finder; +use Sineflow\ElasticsearchBundle\Result\DocumentIterator; /** * Class ScrollAdapter */ class ScrollAdapter { - /** - * @var Finder - */ - private $finder; - - /** - * @var array - */ - private $documentClasses; - - /** - * @var string - */ - private $scrollId; - - /** - * @var string - */ - private $scrollTime; - - /** - * @var int - */ - private $resultsType; - - /** - * @var int|null - */ - private $totalHits; + private string $scrollId; + private readonly int $resultsType; + private ?int $totalHits = null; /** * When a search query with a 'scroll' param is performed, not only the scroll id is returned, but also the * initial batch of results, so we'll cache those here to be returned on the first call to getNextScrollResults() - * - * @var array */ - private $initialResults; + private ?array $initialResults; /** - * @param array $rawResults The raw results from the initial search call - * @param int $resultsType - * @param string $scrollTime The value for the 'scroll' param in a scroll request + * @param array $rawResults The raw results from the initial search call + * @param string $scrollTime The value for the 'scroll' param in a scroll request */ - public function __construct(Finder $finder, array $documentClasses, $rawResults, $resultsType, $scrollTime) - { - $this->finder = $finder; - $this->documentClasses = $documentClasses; + public function __construct( + private readonly Finder $finder, + private readonly array $documentClasses, + array $rawResults, + int $resultsType, + private readonly string $scrollTime, + ) { $this->scrollId = $rawResults['_scroll_id']; - $this->scrollTime = $scrollTime; $this->initialResults = $rawResults; // Make sure we don't get an adapter returned again when we recursively execute the paginated find() $this->resultsType = $resultsType & ~Finder::BITMASK_RESULT_ADAPTERS; @@ -66,10 +40,8 @@ public function __construct(Finder $finder, array $documentClasses, $rawResults, /** * Returns results from a scroll request - * - * @return mixed */ - public function getNextScrollResults() + public function getNextScrollResults(): array|DocumentIterator|false { // If this is the first call to this method, return the cached initial results from the search request if (null !== $this->initialResults) { diff --git a/src/Finder/Finder.php b/src/Finder/Finder.php index 30fa5e7..3f0cd56 100644 --- a/src/Finder/Finder.php +++ b/src/Finder/Finder.php @@ -2,6 +2,8 @@ namespace Sineflow\ElasticsearchBundle\Finder; +use Psr\Cache\InvalidArgumentException; +use Sineflow\ElasticsearchBundle\Document\DocumentInterface; use Sineflow\ElasticsearchBundle\DTO\IndicesToDocumentClasses; use Sineflow\ElasticsearchBundle\Finder\Adapter\KnpPaginatorAdapter; use Sineflow\ElasticsearchBundle\Finder\Adapter\ScrollAdapter; @@ -27,42 +29,21 @@ class Finder public const SCROLL_TIME = '1m'; - /** - * @var DocumentMetadataCollector - */ - private $documentMetadataCollector; - - /** - * @var IndexManagerRegistry - */ - private $indexManagerRegistry; - - /** - * @var DocumentConverter - */ - private $documentConverter; - - /** - * Finder constructor. - */ public function __construct( - DocumentMetadataCollector $documentMetadataCollector, - IndexManagerRegistry $indexManagerRegistry, - DocumentConverter $documentConverter + private readonly DocumentMetadataCollector $documentMetadataCollector, + private readonly IndexManagerRegistry $indexManagerRegistry, + private readonly DocumentConverter $documentConverter, ) { - $this->documentMetadataCollector = $documentMetadataCollector; - $this->indexManagerRegistry = $indexManagerRegistry; - $this->documentConverter = $documentConverter; } /** * Returns a document by identifier * - * @param string $documentClass FQN or short notation (i.e App:Product) + * @param string $documentClass FQN or short notation (i.e. App:Product) * - * @return mixed + * @throws InvalidArgumentException */ - public function get(string $documentClass, string $id, int $resultType = self::RESULTS_OBJECT) + public function get(string $documentClass, string|int $id, int $resultType = self::RESULTS_OBJECT): DocumentInterface|array|null { $indexManagerName = $this->documentMetadataCollector->getDocumentClassIndex($documentClass); @@ -80,16 +61,12 @@ public function get(string $documentClass, string $id, int $resultType = self::R return null; } - switch ($resultType & self::BITMASK_RESULT_TYPES) { - case self::RESULTS_OBJECT: - return $this->documentConverter->convertToDocument($rawDoc, $documentClass); - case self::RESULTS_ARRAY: - return $this->convertToNormalizedArray($rawDoc); - case self::RESULTS_RAW: - return $rawDoc; - default: - throw new \InvalidArgumentException('Wrong result type selected'); - } + return match ($resultType & self::BITMASK_RESULT_TYPES) { + self::RESULTS_OBJECT => $this->documentConverter->convertToDocument($rawDoc, $documentClass), + self::RESULTS_ARRAY => $this->convertToNormalizedArray($rawDoc), + self::RESULTS_RAW => $rawDoc, + default => throw new \InvalidArgumentException('Wrong result type selected'), + }; } /** @@ -99,11 +76,9 @@ public function get(string $documentClass, string $id, int $resultType = self::R * @param array $searchBody The body of the search request * @param int $resultsType Bitmask value determining how the results are returned * @param array $additionalRequestParams Additional params to pass to the ES client's search() method - * @param int $totalHits (out param) The total hits of the query response - * - * @return mixed + * @param int|null $totalHits (out param) The total hits of the query response */ - public function find(array $documentClasses, array $searchBody, $resultsType = self::RESULTS_OBJECT, array $additionalRequestParams = [], &$totalHits = null) + public function find(array $documentClasses, array $searchBody, int $resultsType = self::RESULTS_OBJECT, array $additionalRequestParams = [], ?int &$totalHits = null): array|KnpPaginatorAdapter|ScrollAdapter|DocumentIterator { if (($resultsType & self::BITMASK_RESULT_ADAPTERS) === self::ADAPTER_KNP) { return new KnpPaginatorAdapter($this, $documentClasses, $searchBody, $resultsType, $additionalRequestParams); @@ -151,10 +126,8 @@ public function find(array $documentClasses, array $searchBody, $resultsType = s * @param string $scrollTime The time to keep the scroll window open * @param int $resultsType Bitmask value determining how the results are returned * @param int|null $totalHits (out param) The total hits of the query response - * - * @return mixed */ - public function scroll(array $documentClasses, string &$scrollId, string $scrollTime = self::SCROLL_TIME, int $resultsType = self::RESULTS_OBJECT, int &$totalHits = null) + public function scroll(array $documentClasses, string &$scrollId, string $scrollTime = self::SCROLL_TIME, int $resultsType = self::RESULTS_OBJECT, ?int &$totalHits = null): array|bool|DocumentIterator { $client = $this->getConnection($documentClasses)->getClient(); @@ -232,10 +205,8 @@ public function getTargetIndices(array $documentClasses): array * @param array $raw The raw results as received from Elasticsearch * @param int $resultsType Bitmask value determining how the results are returned * @param string[] $documentClasses The ES entity classes that may be returned from the search - * - * @return array|DocumentIterator */ - public function parseResult(array $raw, int $resultsType, array $documentClasses = null) + public function parseResult(array $raw, int $resultsType, ?array $documentClasses = null): array|DocumentIterator { switch ($resultsType & self::BITMASK_RESULT_TYPES) { case self::RESULTS_OBJECT: diff --git a/src/Manager/ConnectionManager.php b/src/Manager/ConnectionManager.php index 8492a47..d0d850e 100644 --- a/src/Manager/ConnectionManager.php +++ b/src/Manager/ConnectionManager.php @@ -17,72 +17,42 @@ */ class ConnectionManager { - /** - * @var string The unique connection manager name (the key from the index configuration) - */ - protected $connectionName; - - /** - * @var Client - */ - protected $client; - - /** - * @var array - */ - protected $connectionSettings; + protected ?Client $client = null; /** * @var BulkQueryItem[] Container for bulk queries. */ - protected $bulkQueries; - - /** - * @var array Holder for consistency, refresh and replication parameters. - */ - protected $bulkParams; + protected array $bulkQueries; /** - * @var LoggerInterface + * Holder for consistency, refresh and replication parameters. */ - protected $logger; + protected array $bulkParams; - /** - * @var LoggerInterface - */ - protected $tracer; + protected ?LoggerInterface $logger = null; - /** - * @var bool - */ - protected $autocommit; + protected ?LoggerInterface $tracer = null; - /** - * @var EventDispatcherInterface - */ - protected $eventDispatcher; + protected bool $autocommit; - /** - * @var bool - */ - protected $kernelDebug; + protected ?EventDispatcherInterface $eventDispatcher = null; /** * @param string $connectionName The unique connection name * @param array $connectionSettings Settings array * @param bool $kernelDebug The kernel.debug value */ - public function __construct(string $connectionName, array $connectionSettings, bool $kernelDebug) - { - $this->connectionName = $connectionName; - $this->connectionSettings = $connectionSettings; + public function __construct( + protected string $connectionName, + protected array $connectionSettings, + protected bool $kernelDebug, + ) { $this->bulkQueries = []; $this->bulkParams = []; $this->autocommit = false; - $this->kernelDebug = $kernelDebug; } - public function setLogger(LoggerInterface $logger) + public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; } @@ -92,7 +62,7 @@ public function getLogger(): ?LoggerInterface return $this->logger; } - public function setTracer(LoggerInterface $tracer) + public function setTracer(LoggerInterface $tracer): void { $this->tracer = $tracer; } @@ -137,7 +107,7 @@ public function isAutocommit(): bool return $this->autocommit; } - public function setAutocommit(bool $autocommit) + public function setAutocommit(bool $autocommit): void { // If the autocommit mode is being turned on, commit any pending bulk items if (!$this->autocommit && $autocommit) { @@ -152,7 +122,7 @@ public function getEventDispatcher(): EventDispatcherInterface return $this->eventDispatcher; } - public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): void { $this->eventDispatcher = $eventDispatcher; } @@ -163,9 +133,9 @@ public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) * @param string $operation One of: index, update, delete, create. * @param string $index Elasticsearch index name. * @param array $query Bulk item query (aka optional_source in the ES docs) - * @param array $metaParams Additional meta data params for the bulk item + * @param array $metaParams Additional metadata params for the bulk item */ - public function addBulkOperation(string $operation, string $index, array $query, array $metaParams = []) + public function addBulkOperation(string $operation, string $index, array $query, array $metaParams = []): void { $this->bulkQueries[] = new BulkQueryItem($operation, $index, $query, $metaParams); } @@ -186,7 +156,7 @@ public function getBulkOperationsCount(): int * ['refresh'] = (boolean) Refresh the index after performing the operation. * ['replication'] = (enum) Explicitly set the replication type. */ - public function setBulkParams(array $params) + public function setBulkParams(array $params): void { $this->bulkParams = $params; } @@ -201,7 +171,7 @@ public function setBulkParams(array $params) * * @throws BulkRequestException */ - public function commit(bool $forceRefresh = true) + public function commit(bool $forceRefresh = true): void { if (empty($this->bulkQueries)) { return; @@ -216,9 +186,7 @@ public function commit(bool $forceRefresh = true) $this->bulkQueries = []; - if ($this->eventDispatcher) { - $this->eventDispatcher->dispatch(new PostCommitEvent($response, $this), Events::POST_COMMIT); - } + $this->eventDispatcher?->dispatch(new PostCommitEvent($response, $this), Events::POST_COMMIT); if ($response['errors']) { $errorCount = $this->logBulkRequestErrors($response['items']); @@ -285,7 +253,7 @@ private function logBulkRequestErrors(array $responseItems): int /** * Refresh all indices, making any newly indexed documents immediately available for search */ - public function refresh() + public function refresh(): void { // Use an index wildcard to avoid deprecation warnings about accessing system indices // Passing this 'index' argument should not be necessary in ES8 @@ -298,7 +266,7 @@ public function refresh() * Causes a Lucene commit to happen * In most cases refresh() should be used instead, as this is a very expensive operation */ - public function flush() + public function flush(): void { $this->getClient()->indices()->flush(); } @@ -343,7 +311,7 @@ public function existsIndexOrAlias(array $params): bool throw new InvalidArgumentException('Required parameter "index" missing'); } - $indicesAndAliasesToCheck = \array_flip(\explode(',', $params['index'])); + $indicesAndAliasesToCheck = \array_flip(\explode(',', (string) $params['index'])); // Get all available indices (exc. dot-prefixed indices) with their aliases // Passing this 'index' argument should not be necessary in ES8 @@ -385,12 +353,12 @@ public function existsAlias(array $params): bool throw new InvalidArgumentException('Required parameter "name" missing'); } - $aliasesToCheck = \explode(',', $params['name']); + $aliasesToCheck = \explode(',', (string) $params['name']); // Get all available indices (exc. dot-prefixed indices) with their aliases // Passing this 'index' argument should not be necessary in ES8 $allAliases = $this->getClient()->indices()->getAlias(['index' => '*,-.*']); - foreach ($allAliases as $index => $data) { + foreach ($allAliases as $data) { foreach ($aliasesToCheck as $aliasToCheck) { if (isset($data['aliases'][$aliasToCheck])) { return true; diff --git a/src/Manager/IndexManager.php b/src/Manager/IndexManager.php index d8808ec..ec19acb 100644 --- a/src/Manager/IndexManager.php +++ b/src/Manager/IndexManager.php @@ -4,6 +4,7 @@ use Elasticsearch\Common\Exceptions\ElasticsearchException; use Elasticsearch\Common\Exceptions\Missing404Exception; +use Psr\Cache\InvalidArgumentException; use Sineflow\ElasticsearchBundle\Document\DocumentInterface; use Sineflow\ElasticsearchBundle\Document\Provider\ProviderInterface; use Sineflow\ElasticsearchBundle\Document\Provider\ProviderRegistry; @@ -27,93 +28,40 @@ */ class IndexManager { - /** - * @var string The unique manager name (the key from the index configuration) - */ - protected $managerName; - - /** - * @var ConnectionManager Elasticsearch connection. - */ - protected $connection; - - /** - * @var DocumentMetadataCollector - */ - protected $metadataCollector; - - /** - * @var ProviderRegistry - */ - protected $providerRegistry; + protected ?array $indexMapping = null; + private array $indexSettings; + protected Repository $repository; /** - * @var Finder + * Whether to use index aliases */ - protected $finder; + protected bool $useAliases = true; /** - * @var DocumentConverter + * The alias where data should be read from */ - protected $documentConverter; + protected string $readAlias; /** - * @var RepositoryFactory + * The alias where data should be written to */ - protected $repositoryFactory; + protected string $writeAlias; - /** - * @var array - */ - protected $indexMapping; - - /** - * @var array - */ - private $indexSettings; - - /** - * @var Repository - */ - protected $repository; - - /** - * @var bool Whether to use index aliases - */ - protected $useAliases = true; - - /** - * @var string The alias where data should be read from - */ - protected $readAlias; - - /** - * @var string The alias where data should be written to - */ - protected $writeAlias; - - /** - * @var EventDispatcherInterface - */ - protected $eventDispatcher; + protected ?EventDispatcherInterface $eventDispatcher = null; public function __construct( - string $managerName, + /** + * The unique manager name (the key from the index configuration) + */ + protected string $managerName, array $indexSettings, - ConnectionManager $connection, - DocumentMetadataCollector $metadataCollector, - ProviderRegistry $providerRegistry, - Finder $finder, - DocumentConverter $documentConverter, - RepositoryFactory $repositoryFactory + protected ConnectionManager $connection, + protected DocumentMetadataCollector $metadataCollector, + protected ProviderRegistry $providerRegistry, + protected Finder $finder, + protected DocumentConverter $documentConverter, + protected RepositoryFactory $repositoryFactory, ) { - $this->managerName = $managerName; - $this->connection = $connection; - $this->metadataCollector = $metadataCollector; - $this->providerRegistry = $providerRegistry; - $this->finder = $finder; - $this->documentConverter = $documentConverter; - $this->repositoryFactory = $repositoryFactory; $this->useAliases = $indexSettings['use_aliases']; $this->indexSettings = $indexSettings; @@ -130,7 +78,7 @@ public function getEventDispatcher(): EventDispatcherInterface return $this->eventDispatcher; } - public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): void { $this->eventDispatcher = $eventDispatcher; } @@ -339,7 +287,7 @@ public function getLiveIndex(): string * * @throws Exception */ - public function createIndex() + public function createIndex(): void { if (true === $this->getUseAliases()) { // Make sure the read and write aliases do not exist already as aliases or physical indices @@ -376,7 +324,7 @@ public function createIndex() /** * Drops elasticsearch index(es). */ - public function dropIndex() + public function dropIndex(): void { try { if (true === $this->getUseAliases()) { @@ -387,7 +335,7 @@ public function dropIndex() } else { $this->getConnection()->getClient()->indices()->delete(['index' => $this->getBaseIndexName()]); } - } catch (Missing404Exception $e) { + } catch (Missing404Exception) { // No physical indices exist for the index manager's aliases, or the physical index did not exist } } @@ -401,7 +349,7 @@ public function dropIndex() * * @throws ElasticsearchException */ - public function rebuildIndex($deleteOld = false, $cancelExistingRebuild = false) + public function rebuildIndex(bool $deleteOld = false, bool $cancelExistingRebuild = false): void { try { if (false === $this->getUseAliases()) { @@ -462,9 +410,9 @@ public function rebuildIndex($deleteOld = false, $cancelExistingRebuild = false) * Rebuilds the data of a document and adds it to a bulk request for the next commit. * Depending on the connection autocommit mode, the change may be committed right away. * - * @param string|int $id + * @throws InvalidArgumentException */ - public function reindex(string $id) + public function reindex(string|int $id): void { $documentClass = $this->getDocumentClass(); @@ -473,8 +421,8 @@ public function reindex(string $id) switch (true) { case $document instanceof DocumentInterface: - if (\get_class($document) !== $documentClass) { - throw new Exception(\sprintf('Document must be [%s], but [%s] was returned from data provider', $documentClass, \get_class($document))); + if ($document::class !== $documentClass) { + throw new Exception(\sprintf('Document must be [%s], but [%s] was returned from data provider', $documentClass, $document::class)); } $this->persist($document); break; @@ -501,16 +449,14 @@ public function reindex(string $id) /** * Adds document removal to a bulk request for the next commit. * Depending on the connection autocommit mode, the removal may be committed right away. - * - * @param string $id Document ID to remove. */ - public function delete(string $id) + public function delete(string|int $id): void { $this->getConnection()->addBulkOperation( 'delete', $this->writeAlias, [], - ['_id' => $id] + ['_id' => $id], ); if ($this->getConnection()->isAutocommit()) { @@ -521,20 +467,20 @@ public function delete(string $id) /** * Adds a document update to a bulk request for the next commit. * - * @param string $id Document id to update. - * @param array $fields Fields array to update (ignored if script is specified). - * @param null $script Script to update fields. - * @param array $queryParams Additional params to pass with the payload (upsert, doc_as_upsert, _source, etc.) - * @param array $metaParams Additional params to pass with the meta data in the bulk request (_version, _routing, etc.) + * @param string|int $id Document id to update. + * @param array $fields Fields array to update (ignored if script is specified). + * @param null $script Script to update fields. + * @param array $queryParams Additional params to pass with the payload (upsert, doc_as_upsert, _source, etc.) + * @param array $metaParams Additional params to pass with the meta data in the bulk request (_version, _routing, etc.) */ - public function update(string $id, array $fields = [], $script = null, array $queryParams = [], array $metaParams = []) + public function update(string|int $id, array $fields = [], $script = null, array $queryParams = [], array $metaParams = []): void { // Add the id of the updated document to the meta params for the bulk request $metaParams = \array_merge( $metaParams, [ '_id' => $id, - ] + ], ); $query = \array_filter(\array_merge( @@ -542,14 +488,14 @@ public function update(string $id, array $fields = [], $script = null, array $qu [ 'doc' => $fields, 'script' => $script, - ] + ], )); $this->getConnection()->addBulkOperation( 'update', $this->writeAlias, $query, - $metaParams + $metaParams, ); if ($this->getConnection()->isAutocommit()) { @@ -562,13 +508,11 @@ public function update(string $id, array $fields = [], $script = null, array $qu * Depending on the connection autocommit mode, the update may be committed right away. * * @param DocumentInterface $document The document entity to index in ES - * @param array $metaParams Additional params to pass with the meta data in the bulk request (_version, _routing, etc.) + * @param array $metaParams Additional params to pass with the metadata in the bulk request (_version, _routing, etc.) */ - public function persist(DocumentInterface $document, array $metaParams = []) + public function persist(DocumentInterface $document, array $metaParams = []): void { - if ($this->eventDispatcher) { - $this->eventDispatcher->dispatch(new PrePersistEvent($document, $this->getConnection()), Events::PRE_PERSIST); - } + $this->eventDispatcher?->dispatch(new PrePersistEvent($document, $this->getConnection()), Events::PRE_PERSIST); $documentArray = $this->documentConverter->convertToArray($document); $this->persistRaw($documentArray, $metaParams); @@ -581,7 +525,7 @@ public function persist(DocumentInterface $document, array $metaParams = []) * @param array $documentArray The document to index in ES * @param array $metaParams Additional params to pass with the meta data in the bulk request (_version, _routing, etc.) */ - public function persistRaw(array $documentArray, array $metaParams = []) + public function persistRaw(array $documentArray, array $metaParams = []): void { // Remove any read-only meta fields from array to be persisted unset($documentArray['_score']); @@ -590,7 +534,7 @@ public function persistRaw(array $documentArray, array $metaParams = []) 'index', $this->writeAlias, $documentArray, - $metaParams + $metaParams, ); if ($this->getConnection()->isAutocommit()) { @@ -601,7 +545,7 @@ public function persistRaw(array $documentArray, array $metaParams = []) /** * Created a new index with a unique name */ - protected function createNewIndexWithUniqueName(string $suffix = null): string + protected function createNewIndexWithUniqueName(?string $suffix = null): string { $settings = $this->getIndexMapping(); $newIndex = $this->getUniqueIndexName($suffix); @@ -616,7 +560,7 @@ protected function createNewIndexWithUniqueName(string $suffix = null): string * * @param string $oldIndex This is not used here but passed in case an overriding class may need it */ - protected function copyDataToNewIndex(string $newIndex, string $oldIndex) + protected function copyDataToNewIndex(string $newIndex, string $oldIndex): void { // Make sure we don't autocommit on every item in the bulk request $autocommit = $this->getConnection()->isAutocommit(); @@ -657,11 +601,11 @@ protected function copyDataToNewIndex(string $newIndex, string $oldIndex) /** * Verify index and aliases state and try to recover if state is not ok * - * @param null $retryForException (internal) Set on recursive calls to the exception class thrown + * @param string|null $retryForException (internal) Set on recursive calls to the exception class thrown * * @return string The live (aka "hot") index name */ - protected function getLiveIndexPreparedForRebuilding(bool $cancelExistingRebuild, $retryForException = null): string + protected function getLiveIndexPreparedForRebuilding(bool $cancelExistingRebuild, ?string $retryForException = null): string { try { // Make sure the index and both aliases are properly set @@ -674,7 +618,7 @@ protected function getLiveIndexPreparedForRebuilding(bool $cancelExistingRebuild } } catch (IndexOrAliasNotFoundException $e) { // If this is a second attempt with the same exception, then we can't do anything more - if (\get_class($e) === $retryForException) { + if ($e::class === $retryForException) { throw $e; } @@ -682,11 +626,11 @@ protected function getLiveIndexPreparedForRebuilding(bool $cancelExistingRebuild $this->createIndex(); // Now try again - $liveIndex = $this->getLiveIndexPreparedForRebuilding($cancelExistingRebuild, \get_class($e)); + $liveIndex = $this->getLiveIndexPreparedForRebuilding($cancelExistingRebuild, $e::class); } catch (IndexRebuildingException $e) { // If we don't want to cancel the current rebuild or this is a second attempt with the same exception, // then we can't do anything more - if (!$cancelExistingRebuild || (\get_class($e) === $retryForException)) { + if (!$cancelExistingRebuild || ($e::class === $retryForException)) { throw $e; } @@ -696,7 +640,7 @@ protected function getLiveIndexPreparedForRebuilding(bool $cancelExistingRebuild } // Now try again - $liveIndex = $this->getLiveIndexPreparedForRebuilding($cancelExistingRebuild, \get_class($e)); + $liveIndex = $this->getLiveIndexPreparedForRebuilding($cancelExistingRebuild, $e::class); } return $liveIndex; @@ -705,7 +649,7 @@ protected function getLiveIndexPreparedForRebuilding(bool $cancelExistingRebuild /** * Get document metadata * - * @throws \Psr\Cache\InvalidArgumentException + * @throws InvalidArgumentException */ public function getDocumentMetadata(): DocumentMetadata { @@ -715,7 +659,7 @@ public function getDocumentMetadata(): DocumentMetadata /** * Get FQN of document class managed by this index manager * - * @throws \Psr\Cache\InvalidArgumentException + * @throws InvalidArgumentException */ public function getDocumentClass(): string { diff --git a/src/Manager/IndexManagerRegistry.php b/src/Manager/IndexManagerRegistry.php index 6863b22..58aecb1 100644 --- a/src/Manager/IndexManagerRegistry.php +++ b/src/Manager/IndexManagerRegistry.php @@ -12,23 +12,10 @@ */ class IndexManagerRegistry { - /** - * @var DocumentMetadataCollector - */ - private $metadataCollector; - - /** - * @var ServiceLocator - */ - private $serviceLocator; - - /** - * Constructor - */ - public function __construct(DocumentMetadataCollector $metadataCollector, ServiceLocator $serviceLocator) - { - $this->metadataCollector = $metadataCollector; - $this->serviceLocator = $serviceLocator; + public function __construct( + private readonly DocumentMetadataCollector $metadataCollector, + private readonly ServiceLocator $serviceLocator, + ) { } /** @@ -58,7 +45,7 @@ public function get(string $name): IndexManager */ public function getByEntity(DocumentInterface $entity): IndexManager { - return $this->getByClass(get_class($entity)); + return $this->getByClass($entity::class); } /** diff --git a/src/Mapping/Caser.php b/src/Mapping/Caser.php index 3148dfb..2187899 100644 --- a/src/Mapping/Caser.php +++ b/src/Mapping/Caser.php @@ -9,27 +9,19 @@ class Caser { /** * Transforms string to camel case. - * - * @param string $string Text to transform. - * - * @return string */ - public static function camel($string) + public static function camel(string $string): string { return \lcfirst(\str_replace([' ', '_', '-'], '', \ucwords($string, ' _-'))); } /** * Transforms string to snake case. - * - * @param string $string Text to transform. - * - * @return string */ - public static function snake($string) + public static function snake(string $string): string { $string = \preg_replace('#([A-Z\d]+)([A-Z][a-z])#', '\1_\2', self::camel($string)); - $string = \preg_replace('#([a-z\d])([A-Z])#', '\1_\2', $string); + $string = \preg_replace('#([a-z\d])([A-Z])#', '\1_\2', (string) $string); return \strtolower(\strtr($string, '-', '_')); } diff --git a/src/Mapping/DocumentLocator.php b/src/Mapping/DocumentLocator.php index 0a36f35..28bac79 100644 --- a/src/Mapping/DocumentLocator.php +++ b/src/Mapping/DocumentLocator.php @@ -7,17 +7,11 @@ */ class DocumentLocator { - /** - * @var array All locations of Elasticsearch entities available in the application - */ - private $entityLocations = []; - /** * @param array $entityLocations Parameter sfes.entity_locations */ - public function __construct(array $entityLocations) + public function __construct(private array $entityLocations) { - $this->entityLocations = $entityLocations; } /** @@ -46,7 +40,7 @@ public function resolveClassName(string $fullClassName): string if (!\array_key_exists($locationAlias, $this->entityLocations)) { throw new \UnexpectedValueException(\sprintf('Location "%s" does not exist.', $locationAlias)); } - $fullClassName = \rtrim($this->entityLocations[$locationAlias]['namespace'], '\\').'\\'.$className; + $fullClassName = \rtrim((string) $this->entityLocations[$locationAlias]['namespace'], '\\').'\\'.$className; } return $fullClassName; diff --git a/src/Mapping/DocumentMetadata.php b/src/Mapping/DocumentMetadata.php index 00abcac..ee90931 100644 --- a/src/Mapping/DocumentMetadata.php +++ b/src/Mapping/DocumentMetadata.php @@ -12,10 +12,7 @@ class DocumentMetadata public const PROPERTY_ACCESS_PUBLIC = 1; public const PROPERTY_ACCESS_PRIVATE = 2; - /** - * @var array - */ - private $metadata; + private array $metadata; /** * Resolves metadata. @@ -31,7 +28,7 @@ public function __construct(array $metadata) /** * Configures options resolver. */ - protected function configureOptions(OptionsResolver $optionsResolver) + protected function configureOptions(OptionsResolver $optionsResolver): void { $optionsResolver->setRequired(['properties', 'fields', 'propertiesMetadata', 'repositoryClass', 'providerClass', 'className']); } @@ -46,10 +43,8 @@ public function getClientMapping(): array ['properties' => $this->getProperties()], $this->getFields() ), - static function ($value) { - // Remove all empty non-boolean values from the mapping array - return (bool) $value || \is_bool($value); - } + static fn ($value): bool => // Remove all empty non-boolean values from the mapping array + (bool) $value || \is_bool($value) ); return $mapping; diff --git a/src/Mapping/DocumentMetadataCollector.php b/src/Mapping/DocumentMetadataCollector.php index 3a6e0c2..a82587c 100644 --- a/src/Mapping/DocumentMetadataCollector.php +++ b/src/Mapping/DocumentMetadataCollector.php @@ -2,6 +2,7 @@ namespace Sineflow\ElasticsearchBundle\Mapping; +use Psr\Cache\InvalidArgumentException; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; @@ -22,44 +23,21 @@ class DocumentMetadataCollector implements WarmableInterface public const OBJECTS_CACHE_KEY_PREFIX = 'sfes.object_properties_metadata.'; /** - * @var array - * * => */ - private $documentClassToIndexManagerNames = []; - - /** - * @var array - */ - private $indexManagers; - - /** - * @var DocumentLocator - */ - private $documentLocator; - - /** - * @var DocumentParser - */ - private $parser; - - /** - * @var CacheInterface - */ - private $cache; + private array $documentClassToIndexManagerNames = []; /** * @param array $indexManagers The list of index managers defined * @param DocumentParser $parser For reading entity annotations * @param CacheInterface $cache For caching entity metadata */ - public function __construct(array $indexManagers, DocumentLocator $documentLocator, DocumentParser $parser, CacheInterface $cache) - { - $this->indexManagers = $indexManagers; - $this->documentLocator = $documentLocator; - $this->parser = $parser; - $this->cache = $cache; - + public function __construct( + private array $indexManagers, + private readonly DocumentLocator $documentLocator, + private readonly DocumentParser $parser, + private readonly CacheInterface $cache, + ) { // Build an internal array with map of document class to index manager name foreach ($this->indexManagers as $indexManagerName => $indexSettings) { $documentClass = $this->documentLocator->resolveClassName($indexSettings['class']); @@ -75,13 +53,11 @@ public function __construct(array $indexManagers, DocumentLocator $documentLocat /** * Warms up the cache. * - * @param string $cacheDir - * * @return string[] * - * @throws \Psr\Cache\InvalidArgumentException + * @throws InvalidArgumentException */ - public function warmUp($cacheDir) + public function warmUp(string $cacheDir): array { // force cache generation foreach ($this->documentClassToIndexManagerNames as $documentClass => $indexManagerName) { @@ -102,7 +78,7 @@ public function warmUp($cacheDir) * Returns metadata for the specified document class name. * Class can also be specified in short notation (e.g App:Product) * - * @throws \Psr\Cache\InvalidArgumentException + * @throws InvalidArgumentException */ public function getDocumentMetadata(string $documentClass): DocumentMetadata { @@ -110,16 +86,14 @@ public function getDocumentMetadata(string $documentClass): DocumentMetadata $cacheKey = self::DOCUMENTS_CACHE_KEY_PREFIX.\strtr($documentClass, '\\', '.'); - return $this->cache->get($cacheKey, function (ItemInterface $item) use ($documentClass) { - return $this->fetchDocumentMetadata($documentClass); - }, 0); + return $this->cache->get($cacheKey, fn (ItemInterface $item): DocumentMetadata => $this->fetchDocumentMetadata($documentClass), 0); } /** * Returns metadata for the specified object class name * Class can also be specified in short notation (e.g App:ObjCategory) * - * @throws \Psr\Cache\InvalidArgumentException + * @throws InvalidArgumentException */ public function getObjectPropertiesMetadata(string $objectClass): array { @@ -127,9 +101,7 @@ public function getObjectPropertiesMetadata(string $objectClass): array $cacheKey = self::OBJECTS_CACHE_KEY_PREFIX.\strtr($objectClass, '\\', '.'); - return $this->cache->get($cacheKey, function (ItemInterface $item) use ($objectClass) { - return $this->parser->getPropertiesMetadata(new \ReflectionClass($objectClass)); - }, 0); + return $this->cache->get($cacheKey, fn (ItemInterface $item): array => $this->parser->getPropertiesMetadata(new \ReflectionClass($objectClass)), 0); } /** diff --git a/src/Mapping/DocumentParser.php b/src/Mapping/DocumentParser.php index ec6693e..94d2a73 100644 --- a/src/Mapping/DocumentParser.php +++ b/src/Mapping/DocumentParser.php @@ -8,6 +8,7 @@ use Sineflow\ElasticsearchBundle\Annotation\Document; use Sineflow\ElasticsearchBundle\Annotation\Id; use Sineflow\ElasticsearchBundle\Annotation\Property; +use Sineflow\ElasticsearchBundle\Annotation\PropertyAnnotationInterface; use Sineflow\ElasticsearchBundle\Annotation\Score; /** @@ -16,39 +17,19 @@ class DocumentParser { /** - * @var Reader Used to read document annotations. + * Contains gathered objects which later adds to documents. */ - private $reader; + private array $objects = []; /** - * @var DocumentLocator Used to find documents. + * Document properties metadata. */ - private $documentLocator; + private array $propertiesMetadata = []; /** - * @var array Contains gathered objects which later adds to documents. + * Local cache for document properties. */ - private $objects = []; - - /** - * @var array Document properties metadata. - */ - private $propertiesMetadata = []; - - /** - * @var array Local cache for document properties. - */ - private $properties = []; - - /** - * @var string - */ - private $languageSeparator; - - /** - * @var array - */ - private $languages; + private array $properties = []; /** * @param Reader $reader Used for reading annotations @@ -56,23 +37,21 @@ class DocumentParser * @param string $languageSeparator String separating the language code from the ML property name * @param array $languages List of all supported languages */ - public function __construct(Reader $reader, DocumentLocator $documentLocator, string $languageSeparator, array $languages = []) - { - $this->reader = $reader; - $this->documentLocator = $documentLocator; - $this->languageSeparator = $languageSeparator; - $this->languages = $languages; + public function __construct( + private readonly Reader $reader, + private readonly DocumentLocator $documentLocator, + private readonly string $languageSeparator, + private readonly array $languages = [], + ) { $this->registerAnnotations(); } /** * Parses document by used annotations and returns mapping for elasticsearch with some extra metadata. * - * @return array - * * @throws \ReflectionException */ - public function parse(\ReflectionClass $documentReflection, array $indexAnalyzers) + public function parse(\ReflectionClass $documentReflection, array $indexAnalyzers): array { $metadata = []; @@ -98,11 +77,9 @@ public function parse(\ReflectionClass $documentReflection, array $indexAnalyzer /** * Finds properties' metadata for every property used in document or inner/nested object * - * @return array - * * @throws \ReflectionException */ - public function getPropertiesMetadata(\ReflectionClass $documentReflection) + public function getPropertiesMetadata(\ReflectionClass $documentReflection): array { $className = $documentReflection->getName(); if (\array_key_exists($className, $this->propertiesMetadata)) { @@ -113,6 +90,7 @@ public function getPropertiesMetadata(\ReflectionClass $documentReflection) /** @var \ReflectionProperty $property */ foreach ($this->getDocumentPropertiesReflection($documentReflection) as $propertyName => $property) { + /** @var PropertyAnnotationInterface $propertyAnnotation */ $propertyAnnotation = $this->getPropertyAnnotationData($property); $propertyAnnotation = $propertyAnnotation ?: $this->reader->getPropertyAnnotation($property, Id::class); $propertyAnnotation = $propertyAnnotation ?: $this->reader->getPropertyAnnotation($property, Score::class); @@ -122,7 +100,7 @@ public function getPropertiesMetadata(\ReflectionClass $documentReflection) continue; } - switch (\get_class($propertyAnnotation)) { + switch ($propertyAnnotation::class) { case Property::class: $propertyMetadata[$propertyAnnotation->name] = [ 'propertyName' => $propertyName, @@ -148,20 +126,16 @@ public function getPropertiesMetadata(\ReflectionClass $documentReflection) break; case Id::class: - $propertyAnnotation->name = '_id'; - $propertyAnnotation->type = 'keyword'; - $propertyMetadata[$propertyAnnotation->name] = [ + $propertyMetadata[$propertyAnnotation->getName()] = [ 'propertyName' => $propertyName, - 'type' => $propertyAnnotation->type, + 'type' => $propertyAnnotation->getType(), ]; break; case Score::class: - $propertyAnnotation->name = '_score'; - $propertyAnnotation->type = 'float'; - $propertyMetadata[$propertyAnnotation->name] = [ + $propertyMetadata[$propertyAnnotation->getName()] = [ 'propertyName' => $propertyName, - 'type' => $propertyAnnotation->type, + 'type' => $propertyAnnotation->getType(), ]; break; } @@ -174,11 +148,11 @@ public function getPropertiesMetadata(\ReflectionClass $documentReflection) $setterMethod = 'set'.$camelCaseName; $getterMethod = 'get'.$camelCaseName; // Allow issers as getters for boolean properties - if ('boolean' === $propertyAnnotation->type && !$documentReflection->hasMethod($getterMethod)) { + if ('boolean' === $propertyAnnotation->getType() && !$documentReflection->hasMethod($getterMethod)) { $getterMethod = 'is'.$camelCaseName; } if ($documentReflection->hasMethod($getterMethod) && $documentReflection->hasMethod($setterMethod)) { - $propertyMetadata[$propertyAnnotation->name]['methods'] = [ + $propertyMetadata[$propertyAnnotation->getName()]['methods'] = [ 'getter' => $getterMethod, 'setter' => $setterMethod, ]; @@ -188,7 +162,7 @@ public function getPropertiesMetadata(\ReflectionClass $documentReflection) } } - $propertyMetadata[$propertyAnnotation->name]['propertyAccess'] = $propertyAccess; + $propertyMetadata[$propertyAnnotation->getName()]['propertyAccess'] = $propertyAccess; } $this->propertiesMetadata[$className] = $propertyMetadata; @@ -198,23 +172,16 @@ public function getPropertiesMetadata(\ReflectionClass $documentReflection) /** * Returns property annotation data from reader. - * - * @param \ReflectionProperty $property - * - * @return Property|null */ - private function getPropertyAnnotationData($property) + private function getPropertyAnnotationData(\ReflectionProperty $property): ?Property { - /** @var Property $annotation */ - $annotation = $this->reader->getPropertyAnnotation($property, Property::class); - - return $annotation; + return $this->reader->getPropertyAnnotation($property, Property::class); } /** * Registers annotations to registry so that it could be used by reader. */ - private function registerAnnotations() + private function registerAnnotations(): void { $annotations = [ 'Document', @@ -231,10 +198,8 @@ private function registerAnnotations() /** * Returns all defined properties including private from parents. - * - * @return array */ - private function getDocumentPropertiesReflection(\ReflectionClass $documentReflection) + private function getDocumentPropertiesReflection(\ReflectionClass $documentReflection): array { if (\in_array($documentReflection->getName(), $this->properties)) { return $this->properties[$documentReflection->getName()]; @@ -266,15 +231,13 @@ private function getDocumentPropertiesReflection(\ReflectionClass $documentRefle * * @param \ReflectionClass $documentReflection Class to read properties from. * - * @return array - * * @throws \ReflectionException */ - private function getProperties(\ReflectionClass $documentReflection, array $indexAnalyzers = []) + private function getProperties(\ReflectionClass $documentReflection, array $indexAnalyzers = []): array { $mapping = []; /** @var \ReflectionProperty $property */ - foreach ($this->getDocumentPropertiesReflection($documentReflection) as $propertyName => $property) { + foreach ($this->getDocumentPropertiesReflection($documentReflection) as $property) { $propertyAnnotation = $this->getPropertyAnnotationData($property); if (empty($propertyAnnotation)) { @@ -283,23 +246,23 @@ private function getProperties(\ReflectionClass $documentReflection, array $inde // If it is a multi-language property if (true === $propertyAnnotation->multilanguage) { - if (!\in_array($propertyAnnotation->type, ['string', 'keyword', 'text'])) { - throw new \InvalidArgumentException(\sprintf('"%s" property in %s is declared as multilanguage, so can only be of type "keyword", "text" or the deprecated "string"', $propertyAnnotation->name, $documentReflection->getName())); + if (!\in_array($propertyAnnotation->getType(), ['string', 'keyword', 'text'])) { + throw new \InvalidArgumentException(\sprintf('"%s" property in %s is declared as multilanguage, so can only be of type "keyword", "text" or the deprecated "string"', $propertyAnnotation->getName(), $documentReflection->getName())); } if (!$this->languages) { throw new \InvalidArgumentException('There must be at least one language specified in sineflow_elasticsearch.languages in order to use multilanguage properties'); } foreach ($this->languages as $language) { - $mapping[$propertyAnnotation->name.$this->languageSeparator.$language] = $this->getPropertyMapping($propertyAnnotation, $language, $indexAnalyzers); + $mapping[$propertyAnnotation->getName().$this->languageSeparator.$language] = $this->getPropertyMapping($propertyAnnotation, $language, $indexAnalyzers); } // TODO: The application should decide whether it wants to use a default field at all and set its mapping on a global base // The custom mapping from the application should be set here, using perhaps some kind of decorator - $mapping[$propertyAnnotation->name.$this->languageSeparator.Property::DEFAULT_LANG_SUFFIX] = $propertyAnnotation->multilanguageDefaultOptions ?: [ + $mapping[$propertyAnnotation->getName().$this->languageSeparator.Property::DEFAULT_LANG_SUFFIX] = $propertyAnnotation->multilanguageDefaultOptions ?: [ 'type' => 'keyword', 'ignore_above' => 256, ]; } else { - $mapping[$propertyAnnotation->name] = $this->getPropertyMapping($propertyAnnotation, null, $indexAnalyzers); + $mapping[$propertyAnnotation->getName()] = $this->getPropertyMapping($propertyAnnotation, null, $indexAnalyzers); } } @@ -307,11 +270,9 @@ private function getProperties(\ReflectionClass $documentReflection, array $inde } /** - * @return array - * * @throws \ReflectionException */ - private function getPropertyMapping(Property $propertyAnnotation, string $language = null, array $indexAnalyzers = []) + private function getPropertyMapping(Property $propertyAnnotation, ?string $language = null, array $indexAnalyzers = []): array { $propertyMapping = $propertyAnnotation->dump([ 'language' => $language, @@ -331,13 +292,9 @@ private function getPropertyMapping(Property $propertyAnnotation, string $langua * * Loads from cache if it's already loaded. * - * @param string $objectName - * - * @return array - * * @throws \ReflectionException */ - private function getObjectMapping($objectName, array $indexAnalyzers = []) + private function getObjectMapping(string $objectName, array $indexAnalyzers = []): ?array { $className = $this->documentLocator->resolveClassName($objectName); @@ -353,13 +310,9 @@ private function getObjectMapping($objectName, array $indexAnalyzers = []) /** * Returns relation mapping by its reflection. * - * @param array $indexAnalyzers - * - * @return array|null - * * @throws \ReflectionException */ - private function getRelationMapping(\ReflectionClass $documentReflection, $indexAnalyzers = []) + private function getRelationMapping(\ReflectionClass $documentReflection, array $indexAnalyzers = []): ?array { if ($this->reader->getClassAnnotation($documentReflection, DocObject::class)) { return ['properties' => $this->getProperties($documentReflection, $indexAnalyzers)]; diff --git a/src/Mapping/DumperInterface.php b/src/Mapping/DumperInterface.php index 6a09fe5..8ce43b0 100644 --- a/src/Mapping/DumperInterface.php +++ b/src/Mapping/DumperInterface.php @@ -11,8 +11,6 @@ interface DumperInterface * Dumps properties into array. * * @param array $settings Options to configure dump output - * - * @return array */ - public function dump(array $settings = []); + public function dump(array $settings = []): array; } diff --git a/src/Profiler/ElasticsearchProfiler.php b/src/Profiler/ElasticsearchProfiler.php index 1362952..f58e4bc 100644 --- a/src/Profiler/ElasticsearchProfiler.php +++ b/src/Profiler/ElasticsearchProfiler.php @@ -3,6 +3,7 @@ namespace Sineflow\ElasticsearchBundle\Profiler; use Monolog\Logger; +use Monolog\LogRecord; use Sineflow\ElasticsearchBundle\Profiler\Handler\CollectionHandler; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -18,12 +19,12 @@ class ElasticsearchProfiler extends DataCollector /** * @var Logger[] Watched loggers. */ - private $loggers = []; + private array $loggers = []; /** * @var array Registered index managers. */ - private $indexManagers = []; + private array $indexManagers = []; /** * ElasticsearchProfiler constructor. @@ -36,7 +37,7 @@ public function __construct() /** * Adds logger to look for collector handler. */ - public function addLogger(Logger $logger) + public function addLogger(Logger $logger): void { $this->loggers[] = $logger; } @@ -44,7 +45,7 @@ public function addLogger(Logger $logger) /** * {@inheritDoc} */ - public function collect(Request $request, Response $response, \Throwable $exception = null) + public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { $this->data['indexManagers'] = $this->cloneVar($this->indexManagers); @@ -61,7 +62,7 @@ public function collect(Request $request, Response $response, \Throwable $except /** * {@inheritDoc} */ - public function reset() + public function reset(): void { $this->data = [ 'indexManagers' => [], // The list of all defined index managers @@ -106,7 +107,7 @@ public function getIndexManagers(): array return $this->data['indexManagers']->getValue(); } - public function setIndexManagers(array $indexManagers) + public function setIndexManagers(array $indexManagers): void { foreach ($indexManagers as $name => $manager) { $this->indexManagers[$name] = \sprintf('sfes.index.%s', $name); @@ -123,8 +124,13 @@ public function getName(): string /** * Handles passed records. + * + * NOTE: Monolog 3.* passes an array of LogRecord objects instead of arrays, + * but for now we keep compatibility with 2.* as well + * + * @param LogRecord[]|array[] $records */ - private function handleRecords(array $records) + private function handleRecords(array $records): void { $this->data['queryCount'] += \count($records) / 2; $queryBody = ''; @@ -136,14 +142,17 @@ private function handleRecords(array $records) $route = !empty($record['extra']['route']) ? $record['extra']['route'] : self::UNDEFINED_ROUTE; $this->addQuery($route, $record, $queryBody, $rawRequest); } else { - $position = \strpos($record['message'], ' -d'); - $queryBody = false !== $position ? \substr($record['message'], $position + 3) : ''; + $position = \strpos((string) $record['message'], ' -d'); + $queryBody = false !== $position ? \substr((string) $record['message'], $position + 3) : ''; $rawRequest = $record['message']; } } } - private function addQuery(string $route, array $record, string $queryBody, string $rawRequest) + /** + * NOTE: The array typehint of $record is only for BC with Monolog 2.* + */ + private function addQuery(string $route, LogRecord|array $record, string $queryBody, string $rawRequest): void { $parsedUrl = \array_merge( [ @@ -153,7 +162,7 @@ private function addQuery(string $route, array $record, string $queryBody, strin 'path' => '', 'query' => '', ], - \parse_url($record['context']['uri']) + \parse_url((string) $record['context']['uri']) ); $senseRequest = $record['context']['method'].' '.$parsedUrl['path']; if ($parsedUrl['query']) { @@ -170,7 +179,7 @@ private function addQuery(string $route, array $record, string $queryBody, strin 'senseRequest' => $senseRequest, 'backtrace' => $record['extra']['backtrace'], ], - \array_diff_key(\parse_url($record['context']['uri']), \array_flip(['query'])) + \array_diff_key(\parse_url((string) $record['context']['uri']), \array_flip(['query'])) ); } } diff --git a/src/Profiler/Handler/CollectionHandler.php b/src/Profiler/Handler/CollectionHandler.php index 39bf212..176b6ec 100644 --- a/src/Profiler/Handler/CollectionHandler.php +++ b/src/Profiler/Handler/CollectionHandler.php @@ -3,6 +3,7 @@ namespace Sineflow\ElasticsearchBundle\Profiler\Handler; use Monolog\Handler\AbstractProcessingHandler; +use Monolog\LogRecord; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -11,34 +12,18 @@ */ class CollectionHandler extends AbstractProcessingHandler { - /** - * @var array - */ - private $records = []; - - /** - * @var RequestStack - */ - private $requestStack; + private array $records = []; - /** - * @var bool - */ - private $backtraceEnabled; - - /** - * @param bool $backtraceEnabled - */ - public function __construct(RequestStack $requestStack, $backtraceEnabled = false) + public function __construct(private readonly RequestStack $requestStack, private readonly bool $backtraceEnabled = false) { - $this->requestStack = $requestStack; - $this->backtraceEnabled = $backtraceEnabled; } /** * {@inheritdoc} + * + * NOTE: The array typehint of $record is only for BC with Monolog 2.* */ - protected function write(array $record): void + protected function write(LogRecord|array $record): void { $request = $this->requestStack->getCurrentRequest(); if ($request instanceof Request) { @@ -57,9 +42,9 @@ protected function write(array $record): void /** * Returns recorded data. * - * @return array + * @return LogRecord[] */ - public function getRecords() + public function getRecords(): array { return $this->records; } @@ -67,7 +52,7 @@ public function getRecords() /** * Clears recorded data. */ - public function clearRecords() + public function clearRecords(): void { $this->records = []; } diff --git a/src/Result/DocumentConverter.php b/src/Result/DocumentConverter.php index c180531..bc44fcc 100644 --- a/src/Result/DocumentConverter.php +++ b/src/Result/DocumentConverter.php @@ -2,6 +2,7 @@ namespace Sineflow\ElasticsearchBundle\Result; +use Psr\Cache\InvalidArgumentException; use Sineflow\ElasticsearchBundle\Document\DocumentInterface; use Sineflow\ElasticsearchBundle\Document\MLProperty; use Sineflow\ElasticsearchBundle\Document\ObjectInterface; @@ -14,23 +15,13 @@ */ class DocumentConverter { - /** - * @var DocumentMetadataCollector - */ - protected $metadataCollector; - - /** - * @var string - */ - protected $languageSeparator; - /** * Constructor. */ - public function __construct(DocumentMetadataCollector $metadataCollector, string $languageSeparator) - { - $this->metadataCollector = $metadataCollector; - $this->languageSeparator = $languageSeparator; + public function __construct( + protected DocumentMetadataCollector $metadataCollector, + protected string $languageSeparator, + ) { } /** @@ -38,9 +29,9 @@ public function __construct(DocumentMetadataCollector $metadataCollector, string * * @param string $documentClass Document class FQN or in short notation (e.g. App:Product) * - * @return DocumentInterface + * @throws InvalidArgumentException */ - public function convertToDocument(array $rawData, string $documentClass) + public function convertToDocument(array $rawData, string $documentClass): DocumentInterface { // Get document metadata $metadata = $this->metadataCollector->getDocumentMetadata($documentClass); @@ -73,8 +64,8 @@ public function convertToDocument(array $rawData, string $documentClass) } } - /** @var DocumentInterface $document */ $className = $metadata->getClassName(); + /** @var DocumentInterface $document */ $document = $this->assignArrayToObject($data, new $className(), $metadata->getPropertiesMetadata()); return $document; @@ -148,15 +139,14 @@ public function assignArrayToObject(array $array, ObjectInterface $object, array /** * Converts document or (nested) object to an array. * - * @param ObjectInterface $object A document or a (nested) object - * @param array $propertiesMetadata + * @param ObjectInterface $object A document or a (nested) object * - * @throws \ReflectionException + * @throws \ReflectionException|InvalidArgumentException */ - public function convertToArray(ObjectInterface $object, $propertiesMetadata = []): array + public function convertToArray(ObjectInterface $object, array $propertiesMetadata = []): array { if (empty($propertiesMetadata)) { - $propertiesMetadata = $this->metadataCollector->getObjectPropertiesMetadata(\get_class($object)); + $propertiesMetadata = $this->metadataCollector->getObjectPropertiesMetadata($object::class); } $array = []; @@ -205,10 +195,10 @@ public function convertToArray(ObjectInterface $object, $propertiesMetadata = [] * * @throws \InvalidArgumentException */ - private function checkObjectType(ObjectInterface $object, string $expectedClass) + private function checkObjectType(ObjectInterface $object, string $expectedClass): void { - if (\get_class($object) !== $expectedClass) { - throw new \InvalidArgumentException(\sprintf('Expected object of "%s", got "%s"', $expectedClass, \get_class($object))); + if ($object::class !== $expectedClass) { + throw new \InvalidArgumentException(\sprintf('Expected object of "%s", got "%s"', $expectedClass, $object::class)); } } } diff --git a/src/Result/DocumentIterator.php b/src/Result/DocumentIterator.php index fc8f548..72c0db4 100644 --- a/src/Result/DocumentIterator.php +++ b/src/Result/DocumentIterator.php @@ -10,96 +10,50 @@ */ class DocumentIterator implements \Countable, \Iterator { - /** - * @var array - */ - private $rawData; - - /** - * @var DocumentConverter - */ - private $documentConverter; - - /** - * @var IndicesToDocumentClasses - */ - private $indicesToDocumentClasses; - - /** - * @var array - */ - private $suggestions = []; - - /** - * @var array - */ - private $aggregations = []; - - /** - * @var array - */ - private $documents = []; - - /** - * Constructor. - * - * @param array $rawData - */ - public function __construct($rawData, DocumentConverter $documentConverter, IndicesToDocumentClasses $indicesToDocumentClasses) - { - $this->rawData = $rawData; - $this->documentConverter = $documentConverter; - $this->indicesToDocumentClasses = $indicesToDocumentClasses; - - if (isset($rawData['suggest'])) { - $this->suggestions = &$rawData['suggest']; + private array $suggestions = []; + private array $aggregations = []; + private array $documents = []; + + public function __construct( + private array $rawData, + private readonly DocumentConverter $documentConverter, + private readonly IndicesToDocumentClasses $indicesToDocumentClasses, + ) { + if (isset($this->rawData['suggest'])) { + $this->suggestions = &$this->rawData['suggest']; } - if (isset($rawData['aggregations'])) { - $this->aggregations = &$rawData['aggregations']; + if (isset($this->rawData['aggregations'])) { + $this->aggregations = &$this->rawData['aggregations']; } - if (isset($rawData['hits']['hits'])) { - $this->documents = &$rawData['hits']['hits']; + if (isset($this->rawData['hits']['hits'])) { + $this->documents = &$this->rawData['hits']['hits']; } } - /** - * @return array - */ - public function getSuggestions() + public function getSuggestions(): array { return $this->suggestions; } - /** - * @return array - */ - public function getAggregations() + public function getAggregations(): array { return $this->aggregations; } /** * Returns total count of records matching the query. - * - * @return int */ - public function getTotalCount() + public function getTotalCount(): int { return $this->rawData['hits']['total']['value']; } - /** - * @return int - */ - public function count() + public function count(): int { return \count($this->documents); } - /** - * @return DocumentInterface - */ - public function current() + public function current(): ?DocumentInterface { return isset($this->documents[$this->key()]) ? $this->convertToDocument($this->documents[$this->key()]) : null; } @@ -107,7 +61,7 @@ public function current() /** * {@inheritdoc} */ - public function next() + public function next(): void { \next($this->documents); } @@ -115,7 +69,7 @@ public function next() /** * {@inheritdoc} */ - public function key() + public function key(): ?int { return \key($this->documents); } @@ -123,7 +77,7 @@ public function key() /** * {@inheritdoc} */ - public function valid() + public function valid(): bool { return null !== $this->key(); } @@ -131,7 +85,7 @@ public function valid() /** * {@inheritdoc} */ - public function rewind() + public function rewind(): void { \reset($this->documents); } @@ -139,13 +93,9 @@ public function rewind() /** * Converts raw array to document. * - * @param array $rawData - * - * @return DocumentInterface - * * @throws \LogicException */ - private function convertToDocument($rawData) + private function convertToDocument(array $rawData): DocumentInterface { $documentClass = $this->indicesToDocumentClasses->get($rawData['_index']); diff --git a/src/Result/ObjectIterator.php b/src/Result/ObjectIterator.php index e0d4a16..94d2f60 100644 --- a/src/Result/ObjectIterator.php +++ b/src/Result/ObjectIterator.php @@ -9,43 +9,19 @@ */ class ObjectIterator implements \Countable, \Iterator { - /** - * @var array property metadata information. - */ - private $propertyMetadata; - - /** - * @var DocumentConverter - */ - private $documentConverter; - - /** - * @var array - */ - private $objects; - - /** - * Constructor. - */ - public function __construct(DocumentConverter $documentConverter, array $rawData, array $propertyMetadata) - { - $this->documentConverter = $documentConverter; - $this->propertyMetadata = $propertyMetadata; - $this->objects = $rawData; + public function __construct( + private readonly DocumentConverter $documentConverter, + private array $objects, + private readonly array $propertyMetadata, + ) { } - /** - * @return int - */ - public function count() + public function count(): int { return \count($this->objects); } - /** - * @return ObjectInterface - */ - public function current() + public function current(): ?ObjectInterface { return isset($this->objects[$this->key()]) ? $this->convertToObject($this->objects[$this->key()]) : null; } @@ -53,7 +29,7 @@ public function current() /** * {@inheritdoc} */ - public function next() + public function next(): void { \next($this->objects); } @@ -61,7 +37,7 @@ public function next() /** * {@inheritdoc} */ - public function key() + public function key(): int|string|null { return \key($this->objects); } @@ -69,7 +45,7 @@ public function key() /** * {@inheritdoc} */ - public function valid() + public function valid(): bool { return null !== $this->key(); } @@ -77,15 +53,12 @@ public function valid() /** * {@inheritdoc} */ - public function rewind() + public function rewind(): void { \reset($this->objects); } - /** - * {@inheritdoc} - */ - private function convertToObject($rawData) + private function convertToObject($rawData): ObjectInterface { return $this->documentConverter->assignArrayToObject( $rawData, diff --git a/src/Subscriber/EntityTrackerSubscriber.php b/src/Subscriber/EntityTrackerSubscriber.php index 95c041a..6a1357a 100644 --- a/src/Subscriber/EntityTrackerSubscriber.php +++ b/src/Subscriber/EntityTrackerSubscriber.php @@ -2,6 +2,7 @@ namespace Sineflow\ElasticsearchBundle\Subscriber; +use Psr\Cache\InvalidArgumentException; use Sineflow\ElasticsearchBundle\Event\Events; use Sineflow\ElasticsearchBundle\Event\PostCommitEvent; use Sineflow\ElasticsearchBundle\Event\PrePersistEvent; @@ -16,25 +17,13 @@ */ class EntityTrackerSubscriber implements EventSubscriberInterface { - /** - * @var array - */ - private $entitiesData = []; + private array $entitiesData = []; - /** - * @var DocumentMetadataCollector - */ - private $documentMetadataCollector; - - public function __construct(DocumentMetadataCollector $documentMetadataCollector) + public function __construct(private readonly DocumentMetadataCollector $documentMetadataCollector) { - $this->documentMetadataCollector = $documentMetadataCollector; } - /** - * @return array - */ - public static function getSubscribedEvents() + public static function getSubscribedEvents(): array { return [ Events::PRE_PERSIST => 'onPrePersist', @@ -43,13 +32,13 @@ public static function getSubscribedEvents() } /** - * @throws \ReflectionException + * @throws InvalidArgumentException */ - public function onPrePersist(PrePersistEvent $prePersistEvent) + public function onPrePersist(PrePersistEvent $prePersistEvent): void { // Track entity only if it has an @Id field $propertiesMetadata = $this->documentMetadataCollector->getObjectPropertiesMetadata( - \get_class($prePersistEvent->getDocument()) + $prePersistEvent->getDocument()::class ); if (isset($propertiesMetadata['_id'])) { $bulkOperationIndex = $prePersistEvent->getBulkOperationIndex(); @@ -58,7 +47,7 @@ public function onPrePersist(PrePersistEvent $prePersistEvent) } } - public function onPostCommit(PostCommitEvent $postCommitEvent) + public function onPostCommit(PostCommitEvent $postCommitEvent): void { // No need to do anything if there are no persisted entities for that connection if (empty($this->entitiesData[$postCommitEvent->getConnectionName()])) { diff --git a/src/Subscriber/KnpPaginateQuerySubscriber.php b/src/Subscriber/KnpPaginateQuerySubscriber.php index b8ccf30..dcaf4ef 100644 --- a/src/Subscriber/KnpPaginateQuerySubscriber.php +++ b/src/Subscriber/KnpPaginateQuerySubscriber.php @@ -15,14 +15,8 @@ */ class KnpPaginateQuerySubscriber implements EventSubscriberInterface { - /** - * @var RequestStack - */ - private $requestStack; - - public function __construct(RequestStack $requestStack) + public function __construct(private readonly RequestStack $requestStack) { - $this->requestStack = $requestStack; } /** @@ -84,7 +78,7 @@ protected function getSorting(ItemsEvent $event) if ($request instanceof Request) { $sortField = isset($event->options['sortFieldParameterName']) ? $request->get($event->options['sortFieldParameterName']) : null; $sortDirection = isset($event->options['sortDirectionParameterName']) ? $request->get($event->options['sortDirectionParameterName'], 'desc') : null; - $sortDirection = 'desc' === \strtolower($sortDirection) ? 'desc' : 'asc'; + $sortDirection = 'desc' === \strtolower((string) $sortDirection) ? 'desc' : 'asc'; if ($sortField) { // check if the requested sort field is in the sort whitelist diff --git a/tests/AbstractElasticsearchTestCase.php b/tests/AbstractElasticsearchTestCase.php index ab37078..a9f3702 100644 --- a/tests/AbstractElasticsearchTestCase.php +++ b/tests/AbstractElasticsearchTestCase.php @@ -65,10 +65,10 @@ protected function tearDown(): void { parent::tearDown(); - foreach ($this->indexManagers as $name => $indexManager) { + foreach ($this->indexManagers as $indexManager) { try { $indexManager->dropIndex(); - } catch (\Exception $e) { + } catch (\Exception) { // Do nothing. } } diff --git a/tests/App/AppKernel.php b/tests/App/AppKernel.php index f058abf..941e1a9 100644 --- a/tests/App/AppKernel.php +++ b/tests/App/AppKernel.php @@ -10,9 +10,6 @@ use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\HttpKernel\Kernel; -/** - * AppKernel class. - */ class AppKernel extends Kernel { /** @@ -20,7 +17,7 @@ class AppKernel extends Kernel * * @return array */ - public function registerBundles() + public function registerBundles(): iterable { $bundles = [ new FrameworkBundle(), @@ -41,7 +38,7 @@ public function registerBundles() * * @throws \Exception */ - public function registerContainerConfiguration(LoaderInterface $loader) + public function registerContainerConfiguration(LoaderInterface $loader): void { $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); } diff --git a/tests/App/fixture/Acme/FooBundle/Document/Provider/CustomerProvider.php b/tests/App/fixture/Acme/FooBundle/Document/Provider/CustomerProvider.php index f904f13..62289f8 100644 --- a/tests/App/fixture/Acme/FooBundle/Document/Provider/CustomerProvider.php +++ b/tests/App/fixture/Acme/FooBundle/Document/Provider/CustomerProvider.php @@ -2,12 +2,13 @@ namespace Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\FooBundle\Document\Provider; +use Sineflow\ElasticsearchBundle\Document\DocumentInterface; use Sineflow\ElasticsearchBundle\Document\Provider\AbstractProvider; use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\FooBundle\Document\Customer; class CustomerProvider extends AbstractProvider { - private $fixedDocuments = [ + private array $fixedDocuments = [ 1 => [ 'id' => 1, 'name' => 'John', @@ -18,14 +19,14 @@ class CustomerProvider extends AbstractProvider ], ]; - public function getDocuments() + public function getDocuments(): \Generator { foreach ($this->fixedDocuments as $id => $data) { yield $this->getDocument($id); } } - public function getDocument($id) + public function getDocument(int|string $id): DocumentInterface|array|null { if (!isset($this->fixedDocuments[$id])) { return null; diff --git a/tests/App/fixture/Acme/FooBundle/Document/Provider/OrderProvider.php b/tests/App/fixture/Acme/FooBundle/Document/Provider/OrderProvider.php index 2254fd6..282c780 100644 --- a/tests/App/fixture/Acme/FooBundle/Document/Provider/OrderProvider.php +++ b/tests/App/fixture/Acme/FooBundle/Document/Provider/OrderProvider.php @@ -2,12 +2,13 @@ namespace Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\FooBundle\Document\Provider; +use Sineflow\ElasticsearchBundle\Document\DocumentInterface; use Sineflow\ElasticsearchBundle\Document\Provider\AbstractProvider; use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\FooBundle\Document\Order; class OrderProvider extends AbstractProvider { - private $fixedDocuments = [ + private array $fixedDocuments = [ 1 => [ 'id' => 1, 'order_time' => 1452250000, @@ -18,14 +19,14 @@ class OrderProvider extends AbstractProvider ], ]; - public function getDocuments() + public function getDocuments(): \Generator { foreach ($this->fixedDocuments as $id => $data) { yield $this->getDocument($id); } } - public function getDocument($id) + public function getDocument(int|string $id): DocumentInterface|array|null { if (!isset($this->fixedDocuments[$id])) { return null; diff --git a/tests/Functional/Command/IndexBuildCommandTest.php b/tests/Functional/Command/IndexBuildCommandTest.php index 7e2ff09..4c5e8bf 100644 --- a/tests/Functional/Command/IndexBuildCommandTest.php +++ b/tests/Functional/Command/IndexBuildCommandTest.php @@ -13,7 +13,7 @@ class IndexBuildCommandTest extends AbstractCommandTestCase /** * Tests building index */ - public function testExecute() + public function testExecute(): void { $manager = $this->getIndexManager('customer'); diff --git a/tests/Functional/Command/IndexCreateCommandTest.php b/tests/Functional/Command/IndexCreateCommandTest.php index 56152e2..e82218b 100644 --- a/tests/Functional/Command/IndexCreateCommandTest.php +++ b/tests/Functional/Command/IndexCreateCommandTest.php @@ -10,7 +10,7 @@ class IndexCreateCommandTest extends AbstractCommandTestCase /** * Tests creating index */ - public function testExecute() + public function testExecute(): void { $manager = $this->getIndexManager('customer'); @@ -43,7 +43,7 @@ public function testExecute() /** * Tests creating index in case of existing this index. */ - public function testExecuteWithExistingIndex() + public function testExecuteWithExistingIndex(): void { $manager = $this->getIndexManager('customer'); diff --git a/tests/Functional/Document/Provider/ElasticsearchProviderTest.php b/tests/Functional/Document/Provider/ElasticsearchProviderTest.php index 2b5dfb7..44829f1 100644 --- a/tests/Functional/Document/Provider/ElasticsearchProviderTest.php +++ b/tests/Functional/Document/Provider/ElasticsearchProviderTest.php @@ -31,7 +31,7 @@ protected function getDataArray() ]; } - public function testGetDocument() + public function testGetDocument(): void { $esProvider = $this->getProvider(); @@ -43,7 +43,7 @@ public function testGetDocument() ], $doc); } - public function testGetDocuments() + public function testGetDocuments(): void { $esProvider = $this->getProvider(); diff --git a/tests/Functional/Document/Provider/ProviderRegistryTest.php b/tests/Functional/Document/Provider/ProviderRegistryTest.php index 3053dfb..eb32924 100644 --- a/tests/Functional/Document/Provider/ProviderRegistryTest.php +++ b/tests/Functional/Document/Provider/ProviderRegistryTest.php @@ -53,7 +53,7 @@ protected function setUp(): void $this->providerRegistry = $this->getContainer()->get(ProviderRegistry::class); } - public function testGetProviderForEntity() + public function testGetProviderForEntity(): void { $this->assertInstanceOf(CustomerProvider::class, $this->providerRegistry->getCustomProviderForEntity(Customer::class)); $this->assertInstanceOf(OrderProvider::class, $this->providerRegistry->getCustomProviderForEntity(Order::class)); @@ -61,7 +61,7 @@ public function testGetProviderForEntity() $this->assertNull($this->providerRegistry->getCustomProviderForEntity(Product::class)); } - public function testGetSelfProviderForEntity() + public function testGetSelfProviderForEntity(): void { // Get the index managers to trigger the creation of the mock indices $this->getIndexManager('customer'); diff --git a/tests/Functional/Document/RepositoryTest.php b/tests/Functional/Document/RepositoryTest.php index fa10b08..b21c619 100644 --- a/tests/Functional/Document/RepositoryTest.php +++ b/tests/Functional/Document/RepositoryTest.php @@ -70,18 +70,20 @@ protected function setUp(): void $this->getIndexManager('bar', !$this->hasCreatedIndexManager('bar')); } - public function testGetIndexManager() + public function testGetIndexManager(): void { $this->assertInstanceOf(IndexManager::class, $this->repository->getIndexManager()); } - public function testGetById() + public function testGetById(): void { $doc = $this->repository->getById('doc1'); $this->assertEquals('aaa', $doc->title); + $doc = $this->repository->getById(2); + $this->assertEquals('ccc', $doc->title); } - public function testCount() + public function testCount(): void { $searchBody = [ 'query' => [ @@ -94,7 +96,7 @@ public function testCount() $this->assertEquals(2, $this->repository->count($searchBody)); } - public function testReindex() + public function testReindex(): void { $this->assertEquals(1, $this->repository->getById('doc1', Finder::RESULTS_RAW)['_version']); diff --git a/tests/Functional/Finder/Adapter/KnpPaginatorAdapterTest.php b/tests/Functional/Finder/Adapter/KnpPaginatorAdapterTest.php index f524bd2..74f67b5 100644 --- a/tests/Functional/Finder/Adapter/KnpPaginatorAdapterTest.php +++ b/tests/Functional/Finder/Adapter/KnpPaginatorAdapterTest.php @@ -71,7 +71,7 @@ protected function getDataArray() ]; } - public function testPagination() + public function testPagination(): void { /** @var Repository $repo */ $repo = $this->getIndexManager('bar')->getRepository(); @@ -121,7 +121,7 @@ public function testPagination() $this->assertIsArray($pagination->getCustomParameter('suggestions')); } - public function testPaginationSorting() + public function testPaginationSorting(): void { // Create an empty request to get around a bug in KNP paginator that assumes there is always a Request // https://github.com/KnpLabs/knp-components/issues/239 @@ -149,7 +149,7 @@ public function testPaginationSorting() $this->assertEquals(54321, $pagination->current()->id); } - public function testInvalidResultsType() + public function testInvalidResultsType(): void { /** @var Repository $repo */ $repo = $this->getIndexManager('bar')->getRepository(); diff --git a/tests/Functional/Finder/Adapter/ScrollAdapterTest.php b/tests/Functional/Finder/Adapter/ScrollAdapterTest.php index 96e5c3d..4e98f1c 100644 --- a/tests/Functional/Finder/Adapter/ScrollAdapterTest.php +++ b/tests/Functional/Finder/Adapter/ScrollAdapterTest.php @@ -52,7 +52,7 @@ protected function getDataArray() ]; } - public function testScanScroll() + public function testScanScroll(): void { /** @var Repository $repo */ $repo = $this->getIndexManager('bar')->getRepository(); diff --git a/tests/Functional/Finder/FinderTest.php b/tests/Functional/Finder/FinderTest.php index 5721e2a..135141f 100644 --- a/tests/Functional/Finder/FinderTest.php +++ b/tests/Functional/Finder/FinderTest.php @@ -61,7 +61,7 @@ protected function tearDown(): void // Delete the read-only index we manually created $im = $this->getIndexManager('customer', false); $im->getConnection()->getClient()->indices()->delete(['index' => $this->readOnlyIndexName]); - } catch (\Exception $e) { + } catch (\Exception) { // Do nothing. } } @@ -109,7 +109,7 @@ protected function setUp(): void ]); } - public function testGetById() + public function testGetById(): void { /** @var Finder $finder */ $finder = $this->getContainer()->get(Finder::class); @@ -133,7 +133,7 @@ public function testGetById() $this->assertInstanceOf(Product::class, $docAsObjectKNP); } - public function testGetByIdWhenHavingAnotherReadOnlyIndex() + public function testGetByIdWhenHavingAnotherReadOnlyIndex(): void { $finder = $this->getContainer()->get(Finder::class); @@ -143,7 +143,7 @@ public function testGetByIdWhenHavingAnotherReadOnlyIndex() $this->assertInstanceOf(Customer::class, $doc); } - public function testFindInMultipleTypesAndIndices() + public function testFindInMultipleTypesAndIndices(): void { $finder = $this->getContainer()->get(Finder::class); @@ -180,7 +180,7 @@ public function testFindInMultipleTypesAndIndices() $this->assertArrayHasKey('hits', $res['hits']); } - public function testFindForKNPPaginator() + public function testFindForKNPPaginator(): void { $finder = $this->getContainer()->get(Finder::class); @@ -202,7 +202,7 @@ public function testFindForKNPPaginator() $this->assertInstanceOf(KnpPaginatorAdapter::class, $res); } - public function testCount() + public function testCount(): void { $finder = $this->getContainer()->get(Finder::class); @@ -218,7 +218,7 @@ public function testCount() $this->assertEquals(2, $finder->count(['AcmeBarBundle:Product', 'AcmeFooBundle:Customer'], $searchBody)); } - public function testGetTargetIndices() + public function testGetTargetIndices(): void { $finder = $this->getContainer()->get(Finder::class); diff --git a/tests/Functional/Manager/IndexManagerRegistryTest.php b/tests/Functional/Manager/IndexManagerRegistryTest.php index b6da46c..765111f 100644 --- a/tests/Functional/Manager/IndexManagerRegistryTest.php +++ b/tests/Functional/Manager/IndexManagerRegistryTest.php @@ -13,7 +13,7 @@ */ class IndexManagerRegistryTest extends AbstractContainerAwareTestCase { - public function testGet() + public function testGet(): void { /** @var IndexManagerRegistry $registry */ $registry = $this->getContainer()->get(IndexManagerRegistry::class); @@ -28,17 +28,17 @@ public function testGet() $im = $registry->get('nonexisting'); } - public function testGetByClass() + public function testGetByClass(): void { $registry = $this->getContainer()->get(IndexManagerRegistry::class); $product = new Product(); - $im = $registry->getByClass(get_class($product)); + $im = $registry->getByClass($product::class); $this->assertInstanceOf(IndexManager::class, $im); $this->assertEquals('bar', $im->getManagerName()); } - public function testGetByEntity() + public function testGetByEntity(): void { $registry = $this->getContainer()->get(IndexManagerRegistry::class); diff --git a/tests/Functional/Manager/IndexManagerTest.php b/tests/Functional/Manager/IndexManagerTest.php index 9836ee8..81ed2ec 100644 --- a/tests/Functional/Manager/IndexManagerTest.php +++ b/tests/Functional/Manager/IndexManagerTest.php @@ -60,7 +60,7 @@ protected function getDataArray() ]; } - public function testGetReadAliasAndGetWriteAlias() + public function testGetReadAliasAndGetWriteAlias(): void { $imWithAliases = $this->getIndexManager('customer', false); $this->assertEquals('sineflow-esb-test-customer', $imWithAliases->getReadAlias()); @@ -71,7 +71,7 @@ public function testGetReadAliasAndGetWriteAlias() $this->assertEquals('sineflow-esb-test-bar', $imWithoutAliases->getWriteAlias()); } - public function testCreateIndexWithAliases() + public function testCreateIndexWithAliases(): void { $imWithAliases = $this->getIndexManager('customer', false); $imWithAliases->createIndex(); @@ -83,7 +83,7 @@ public function testCreateIndexWithAliases() $this->assertCount(1, $indicesPointedByAliases, 'Read and Write aliases must point to one and the same index'); } - public function testCreateIndexWithoutAliases() + public function testCreateIndexWithoutAliases(): void { $imWithoutAliases = $this->getIndexManager('bar', false); $imWithoutAliases->createIndex(); @@ -93,7 +93,7 @@ public function testCreateIndexWithoutAliases() $this->assertCount(0, \current($index)['aliases'], 'Index should not have any aliases pointing to it'); } - public function testDropIndexWithAliases() + public function testDropIndexWithAliases(): void { $imWithAliases = $this->getIndexManager('customer', false); $imWithAliases->createIndex(); @@ -101,7 +101,7 @@ public function testDropIndexWithAliases() // Simulate state during rebuilding when write alias points to more than 1 index try { $imWithAliases->getConnection()->getClient()->indices()->delete(['index' => 'sineflow-esb-test-temp']); - } catch (\Exception $e) { + } catch (\Exception) { } $imWithAliases->getConnection()->getClient()->indices()->create(['index' => 'sineflow-esb-test-temp']); $setAliasParams = [ @@ -124,7 +124,7 @@ public function testDropIndexWithAliases() $imWithAliases->getConnection()->getClient()->indices()->getAlias(['name' => 'sineflow-esb-test-customer,sineflow-esb-test-customer_write']); } - public function testGetLiveIndexWhenNoIndexExists() + public function testGetLiveIndexWhenNoIndexExists(): void { /** @var IndexManager $imWithAliases */ $imWithAliases = $this->getIndexManager('customer', false); @@ -133,7 +133,7 @@ public function testGetLiveIndexWhenNoIndexExists() $imWithAliases->getLiveIndex(); } - public function testGetLiveIndex() + public function testGetLiveIndex(): void { /** @var IndexManager $imWithAliases */ $imWithAliases = $this->getIndexManager('customer'); @@ -146,14 +146,14 @@ public function testGetLiveIndex() $this->assertEquals('sineflow-esb-test-bar', $liveIndex); } - public function testRebuildIndexWithoutAliases() + public function testRebuildIndexWithoutAliases(): void { $imWithoutAliases = $this->getIndexManager('bar'); $this->expectException(Exception::class); $imWithoutAliases->rebuildIndex(); } - public function testRebuildIndexWithoutDeletingOld() + public function testRebuildIndexWithoutDeletingOld(): void { $imWithAliases = $this->getIndexManager('customer'); $liveIndex = $imWithAliases->getLiveIndex(); @@ -167,7 +167,7 @@ public function testRebuildIndexWithoutDeletingOld() $this->assertNotEquals($liveIndex, $newLiveIndex); } - public function testRebuildIndexAndDeleteOld() + public function testRebuildIndexAndDeleteOld(): void { $imWithAliases = $this->getIndexManager('customer'); $liveIndex = $imWithAliases->getLiveIndex(); @@ -180,7 +180,7 @@ public function testRebuildIndexAndDeleteOld() $this->assertNotEquals($liveIndex, $newLiveIndex); } - public function testPersistForManagerWithoutAliasesWithoutAutocommit() + public function testPersistForManagerWithoutAliasesWithoutAutocommit(): void { $imWithoutAliases = $this->getIndexManager('bar'); $imWithoutAliases->getConnection()->setAutocommit(false); @@ -206,7 +206,7 @@ public function testPersistForManagerWithoutAliasesWithoutAutocommit() $this->assertNull($doc->title, 'Null property value was not persisted'); } - public function testPersistForManagerWithAliasesWithoutAutocommit() + public function testPersistForManagerWithAliasesWithoutAutocommit(): void { $imWithAliases = $this->getIndexManager('customer'); $imWithAliases->getConnection()->setAutocommit(false); @@ -246,7 +246,7 @@ public function testPersistForManagerWithAliasesWithoutAutocommit() $imWithAliases->getConnection()->getClient()->indices()->delete(['index' => 'sineflow-esb-test-temp']); } - public function testPersistRawWithAutocommit() + public function testPersistRawWithAutocommit(): void { $imWithAliases = $this->getIndexManager('customer'); $imWithAliases->getConnection()->setAutocommit(true); @@ -261,7 +261,7 @@ public function testPersistRawWithAutocommit() $this->assertEquals('Jane', $doc->name); } - public function testPersistStrictMappingDocRetrievedById() + public function testPersistStrictMappingDocRetrievedById(): void { $im = $this->getIndexManager('bar'); $im->getConnection()->setAutocommit(true); @@ -275,7 +275,7 @@ public function testPersistStrictMappingDocRetrievedById() $this->assertEquals('NewName', $doc->title); } - public function testUpdateWithCorrectParams() + public function testUpdateWithCorrectParams(): void { $imWithAliases = $this->getIndexManager('customer'); $imWithAliases->getConnection()->setAutocommit(true); @@ -288,7 +288,7 @@ public function testUpdateWithCorrectParams() $this->assertEquals('Alicia', $doc->name); } - public function testUpdateInexistingDoc() + public function testUpdateInexistingDoc(): void { $imWithAliases = $this->getIndexManager('customer'); $imWithAliases->getConnection()->setAutocommit(true); @@ -299,7 +299,7 @@ public function testUpdateInexistingDoc() ]); } - public function testDelete() + public function testDelete(): void { $imWithAliases = $this->getIndexManager('customer'); $imWithAliases->getConnection()->setAutocommit(true); @@ -334,7 +334,7 @@ public function testDelete() ]); } - public function testReindexWithElasticsearchSelfProvider() + public function testReindexWithElasticsearchSelfProvider(): void { $im = $this->getIndexManager('backup'); $im->getConnection()->setAutocommit(false); @@ -354,21 +354,21 @@ public function testReindexWithElasticsearchSelfProvider() $this->assertEquals('log entry', $rawDoc['_source']['entry']); } - public function testGetDataProvider() + public function testGetDataProvider(): void { $imWithAliases = $this->getIndexManager('customer', false); $dataProvider = $imWithAliases->getDataProvider(); $this->assertInstanceOf(CustomerProvider::class, $dataProvider); } - public function testGetDataProviderWhenNoCustomProviderIsSet() + public function testGetDataProviderWhenNoCustomProviderIsSet(): void { $imWithAliases = $this->getIndexManager('bar', false); $dataProvider = $imWithAliases->getDataProvider(); $this->assertInstanceOf(ElasticsearchProvider::class, $dataProvider); } - public function testGetRepository() + public function testGetRepository(): void { $imWithoutAliases = $this->getIndexManager('bar', false); $this->assertInstanceOf(ProductRepository::class, $imWithoutAliases->getRepository()); @@ -377,7 +377,7 @@ public function testGetRepository() $this->assertInstanceOf(Repository::class, $imWithAliases->getRepository()); } - public function testGetters() + public function testGetters(): void { $imWithAliases = $this->getIndexManager('customer', false); $imWithoutAliases = $this->getIndexManager('bar', false); @@ -391,7 +391,7 @@ public function testGetters() $this->assertEquals('bar', $imWithoutAliases->getManagerName()); } - public function testGetDocumentMetadata() + public function testGetDocumentMetadata(): void { /** @var IndexManager $imWithAliases */ $imWithAliases = $this->getIndexManager('customer', false); diff --git a/tests/Functional/Mapping/DocumentMetadataCollectorTest.php b/tests/Functional/Mapping/DocumentMetadataCollectorTest.php index 078bd44..324a257 100644 --- a/tests/Functional/Mapping/DocumentMetadataCollectorTest.php +++ b/tests/Functional/Mapping/DocumentMetadataCollectorTest.php @@ -8,6 +8,12 @@ use Sineflow\ElasticsearchBundle\Mapping\DocumentMetadataCollector; use Sineflow\ElasticsearchBundle\Mapping\DocumentParser; use Sineflow\ElasticsearchBundle\Tests\AbstractContainerAwareTestCase; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\ObjCategory; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\ObjTag; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Product; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Repository\ProductRepository; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\FooBundle\Document\Customer; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\FooBundle\Document\Provider\CustomerProvider; use Symfony\Contracts\Cache\CacheInterface; /** @@ -17,337 +23,314 @@ class DocumentMetadataCollectorTest extends AbstractContainerAwareTestCase { use AssertThrows; - /** - * @var DocumentMetadataCollector - */ - private $metadataCollector; - - /** - * @var array - */ - private $indexManagers; - - /** - * @var DocumentLocator - */ - private $docLocator; - - /** - * @var DocumentParser - */ - private $docParser; - - /** - * @var CacheInterface - */ - private $cache; - - /** - * @var CacheInterface - */ - private $nullCache; + private DocumentMetadataCollector $metadataCollector; + private array $indexManagers; + private DocumentLocator $docLocator; + private DocumentParser $docParser; + private CacheInterface $cache; + private CacheInterface $nullCache; /** * @var array Expected metadata for customer index */ - private $expectedCustomerMetadata = [ + private array $expectedCustomerMetadata = [ 'properties' => [ - 'name' => [ - 'type' => 'keyword', - ], - 'active' => [ - 'type' => 'boolean', - ], + 'name' => [ + 'type' => 'keyword', ], - 'fields' => [ + 'active' => [ + 'type' => 'boolean', ], + ], + 'fields' => [ + ], 'propertiesMetadata' => [ - 'name' => [ - 'propertyName' => 'name', - 'type' => 'keyword', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'active' => [ - 'propertyName' => 'active', - 'type' => 'boolean', - 'multilanguage' => null, - 'methods' => [ - 'getter' => 'isActive', - 'setter' => 'setActive', - ], - 'propertyAccess' => 2, - ], - '_id' => [ - 'propertyName' => 'id', - 'type' => 'keyword', - 'propertyAccess' => 1, - ], - '_score' => [ - 'propertyName' => 'score', - 'type' => 'float', - 'propertyAccess' => 1, - ], + 'name' => [ + 'propertyName' => 'name', + 'type' => 'keyword', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'active' => [ + 'propertyName' => 'active', + 'type' => 'boolean', + 'multilanguage' => null, + 'methods' => [ + 'getter' => 'isActive', + 'setter' => 'setActive', + ], + 'propertyAccess' => 2, ], + '_id' => [ + 'propertyName' => 'id', + 'type' => 'keyword', + 'propertyAccess' => 1, + ], + '_score' => [ + 'propertyName' => 'score', + 'type' => 'float', + 'propertyAccess' => 1, + ], + ], 'repositoryClass' => null, - 'providerClass' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\FooBundle\\Document\\Provider\\CustomerProvider', - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\FooBundle\\Document\\Customer', + 'providerClass' => CustomerProvider::class, + 'className' => Customer::class, ]; - private $expectedProductMetadata = [ + private array $expectedProductMetadata = [ 'properties' => [ - 'title' => [ - 'fields' => [ - 'raw' => [ - 'type' => 'keyword', - ], - 'title' => [ - 'type' => 'text', - ], - ], - 'type' => 'text', + 'title' => [ + 'fields' => [ + 'raw' => [ + 'type' => 'keyword', ], - 'description' => [ + 'title' => [ 'type' => 'text', ], - 'category' => [ - 'properties' => [ - 'id' => [ - 'type' => 'integer', - ], - 'title' => [ - 'type' => 'keyword', - ], - 'tags' => [ - 'properties' => [ - 'tagname' => [ - 'type' => 'text', - ], - ], - ], - ], + ], + 'type' => 'text', + ], + 'description' => [ + 'type' => 'text', + ], + 'category' => [ + 'properties' => [ + 'id' => [ + 'type' => 'integer', + ], + 'title' => [ + 'type' => 'keyword', ], - 'related_categories' => [ + 'tags' => [ 'properties' => [ - 'id' => [ - 'type' => 'integer', - ], - 'title' => [ - 'type' => 'keyword', - ], - 'tags' => [ - 'properties' => [ - 'tagname' => [ - 'type' => 'text', - ], - ], - ], + 'tagname' => [ + 'type' => 'text', ], + ], ], - 'price' => [ - 'type' => 'float', - ], - 'location' => [ - 'type' => 'geo_point', + ], + ], + 'related_categories' => [ + 'properties' => [ + 'id' => [ + 'type' => 'integer', ], - 'limited' => [ - 'type' => 'boolean', + 'title' => [ + 'type' => 'keyword', ], - 'released' => [ - 'type' => 'date', + 'tags' => [ + 'properties' => [ + 'tagname' => [ + 'type' => 'text', + ], + ], ], - 'ml_info-en' => [ + ], + ], + 'price' => [ + 'type' => 'float', + ], + 'location' => [ + 'type' => 'geo_point', + ], + 'limited' => [ + 'type' => 'boolean', + ], + 'released' => [ + 'type' => 'date', + ], + 'ml_info-en' => [ + 'analyzer' => 'en_analyzer', + 'fields' => [ + 'ngram' => [ + 'type' => 'text', 'analyzer' => 'en_analyzer', - 'fields' => [ - 'ngram' => [ - 'type' => 'text', - 'analyzer' => 'en_analyzer', - ], - ], - 'type' => 'text', ], - 'ml_info-fr' => [ + ], + 'type' => 'text', + ], + 'ml_info-fr' => [ + 'analyzer' => 'default_analyzer', + 'fields' => [ + 'ngram' => [ + 'type' => 'text', 'analyzer' => 'default_analyzer', - 'fields' => [ - 'ngram' => [ - 'type' => 'text', - 'analyzer' => 'default_analyzer', - ], - ], - 'type' => 'text', - ], - 'ml_info-default' => [ - 'type' => 'keyword', - 'ignore_above' => 256, - ], - 'ml_more_info-en' => [ - 'type' => 'text', - ], - 'ml_more_info-fr' => [ - 'type' => 'text', - ], - 'ml_more_info-default' => [ - 'type' => 'text', - 'index' => false, - ], - 'pieces_count' => [ - 'fields' => [ - 'count' => [ - 'type' => 'token_count', - 'analyzer' => 'whitespace', - ], - ], - 'type' => 'text', ], + ], + 'type' => 'text', ], - 'fields' => [ - 'dynamic' => 'strict', + 'ml_info-default' => [ + 'type' => 'keyword', + 'ignore_above' => 256, + ], + 'ml_more_info-en' => [ + 'type' => 'text', + ], + 'ml_more_info-fr' => [ + 'type' => 'text', + ], + 'ml_more_info-default' => [ + 'type' => 'text', + 'index' => false, ], + 'pieces_count' => [ + 'fields' => [ + 'count' => [ + 'type' => 'token_count', + 'analyzer' => 'whitespace', + ], + ], + 'type' => 'text', + ], + ], + 'fields' => [ + 'dynamic' => 'strict', + ], 'propertiesMetadata' => [ - 'title' => [ - 'propertyName' => 'title', - 'type' => 'text', + 'title' => [ + 'propertyName' => 'title', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'description' => [ + 'propertyName' => 'description', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'category' => [ + 'propertyName' => 'category', + 'type' => 'object', + 'multilanguage' => null, + 'multiple' => null, + 'propertiesMetadata' => [ + 'id' => [ + 'propertyName' => 'id', + 'type' => 'integer', 'multilanguage' => null, 'propertyAccess' => 1, ], - 'description' => [ - 'propertyName' => 'description', - 'type' => 'text', + 'title' => [ + 'propertyName' => 'title', + 'type' => 'keyword', 'multilanguage' => null, 'propertyAccess' => 1, ], - 'category' => [ - 'propertyName' => 'category', - 'type' => 'object', - 'multilanguage' => null, - 'multiple' => null, - 'propertiesMetadata' => [ - 'id' => [ - 'propertyName' => 'id', - 'type' => 'integer', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'title' => [ - 'propertyName' => 'title', - 'type' => 'keyword', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'tags' => [ - 'propertyName' => 'tags', - 'type' => 'object', - 'multilanguage' => null, - 'multiple' => true, - 'propertiesMetadata' => [ - 'tagname' => [ - 'propertyName' => 'tagName', - 'type' => 'text', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - ], - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\ObjTag', - 'propertyAccess' => 1, - ], - ], - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\ObjCategory', - 'propertyAccess' => 1, - ], - 'related_categories' => [ - 'propertyName' => 'relatedCategories', + 'tags' => [ + 'propertyName' => 'tags', 'type' => 'object', 'multilanguage' => null, 'multiple' => true, 'propertiesMetadata' => [ - 'id' => [ - 'propertyName' => 'id', - 'type' => 'integer', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'title' => [ - 'propertyName' => 'title', - 'type' => 'keyword', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'tags' => [ - 'propertyName' => 'tags', - 'type' => 'object', - 'multilanguage' => null, - 'multiple' => true, - 'propertiesMetadata' => [ - 'tagname' => [ - 'propertyName' => 'tagName', - 'type' => 'text', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - ], - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\ObjTag', - 'propertyAccess' => 1, - ], + 'tagname' => [ + 'propertyName' => 'tagName', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, ], - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\ObjCategory', + ], + 'className' => ObjTag::class, 'propertyAccess' => 1, ], - 'price' => [ - 'propertyName' => 'price', - 'type' => 'float', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'location' => [ - 'propertyName' => 'location', - 'type' => 'geo_point', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'limited' => [ - 'propertyName' => 'limited', - 'type' => 'boolean', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'released' => [ - 'propertyName' => 'released', - 'type' => 'date', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'ml_info' => [ - 'propertyName' => 'mlInfo', - 'type' => 'text', - 'multilanguage' => true, - 'propertyAccess' => 1, - ], - 'ml_more_info' => [ - 'propertyName' => 'mlMoreInfo', - 'type' => 'text', - 'multilanguage' => true, - 'propertyAccess' => 1, - ], - 'pieces_count' => [ - 'propertyName' => 'tokenPiecesCount', - 'type' => 'text', + ], + 'className' => ObjCategory::class, + 'propertyAccess' => 1, + ], + 'related_categories' => [ + 'propertyName' => 'relatedCategories', + 'type' => 'object', + 'multilanguage' => null, + 'multiple' => true, + 'propertiesMetadata' => [ + 'id' => [ + 'propertyName' => 'id', + 'type' => 'integer', 'multilanguage' => null, 'propertyAccess' => 1, ], - '_id' => [ - 'propertyName' => 'id', + 'title' => [ + 'propertyName' => 'title', 'type' => 'keyword', + 'multilanguage' => null, 'propertyAccess' => 1, ], - '_score' => [ - 'propertyName' => 'score', - 'type' => 'float', + 'tags' => [ + 'propertyName' => 'tags', + 'type' => 'object', + 'multilanguage' => null, + 'multiple' => true, + 'propertiesMetadata' => [ + 'tagname' => [ + 'propertyName' => 'tagName', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + ], + 'className' => ObjTag::class, 'propertyAccess' => 1, ], + ], + 'className' => ObjCategory::class, + 'propertyAccess' => 1, + ], + 'price' => [ + 'propertyName' => 'price', + 'type' => 'float', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'location' => [ + 'propertyName' => 'location', + 'type' => 'geo_point', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'limited' => [ + 'propertyName' => 'limited', + 'type' => 'boolean', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'released' => [ + 'propertyName' => 'released', + 'type' => 'date', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'ml_info' => [ + 'propertyName' => 'mlInfo', + 'type' => 'text', + 'multilanguage' => true, + 'propertyAccess' => 1, + ], + 'ml_more_info' => [ + 'propertyName' => 'mlMoreInfo', + 'type' => 'text', + 'multilanguage' => true, + 'propertyAccess' => 1, + ], + 'pieces_count' => [ + 'propertyName' => 'tokenPiecesCount', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + '_id' => [ + 'propertyName' => 'id', + 'type' => 'keyword', + 'propertyAccess' => 1, + ], + '_score' => [ + 'propertyName' => 'score', + 'type' => 'float', + 'propertyAccess' => 1, ], - 'repositoryClass' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\Repository\\ProductRepository', + ], + 'repositoryClass' => ProductRepository::class, 'providerClass' => null, - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\Product', + 'className' => Product::class, ]; protected function setUp(): void @@ -361,10 +344,10 @@ protected function setUp(): void $this->metadataCollector = new DocumentMetadataCollector($this->indexManagers, $this->docLocator, $this->docParser, $this->cache); } - public function testGetDocumentMetadata() + public function testGetDocumentMetadata(): void { $indexMetadataForAlias = $this->metadataCollector->getDocumentMetadata('AcmeFooBundle:Customer'); - $indexMetadata = $this->metadataCollector->getDocumentMetadata('Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\FooBundle\Document\Customer'); + $indexMetadata = $this->metadataCollector->getDocumentMetadata(Customer::class); // Make sure alias and FQN name work the same $this->assertEquals($indexMetadata, $indexMetadataForAlias); @@ -379,21 +362,21 @@ public function testGetDocumentMetadata() ); } - public function testMetadataWithCacheVsNoCache() + public function testMetadataWithCacheVsNoCache(): void { $metadataCollectorWithCacheDisabled = new DocumentMetadataCollector($this->indexManagers, $this->docLocator, $this->docParser, $this->nullCache); $this->assertEquals($this->metadataCollector->getDocumentMetadata('AcmeFooBundle:Customer'), $metadataCollectorWithCacheDisabled->getDocumentMetadata('AcmeFooBundle:Customer')); $this->assertEquals($this->metadataCollector->getObjectPropertiesMetadata('AcmeFooBundle:Customer'), $metadataCollectorWithCacheDisabled->getObjectPropertiesMetadata('AcmeFooBundle:Customer')); } - public function testGetObjectPropertiesMetadataWithValidClasses() + public function testGetObjectPropertiesMetadataWithValidClasses(): void { // Test document's metadata $metadata = $this->metadataCollector->getObjectPropertiesMetadata('AcmeFooBundle:Customer'); $this->assertEquals($this->expectedCustomerMetadata['propertiesMetadata'], $metadata); // Test nested object's metadata - $metadata = $this->metadataCollector->getObjectPropertiesMetadata('Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\ObjTag'); + $metadata = $this->metadataCollector->getObjectPropertiesMetadata(ObjTag::class); $this->assertEquals($this->expectedProductMetadata['propertiesMetadata']['category']['propertiesMetadata']['tags']['propertiesMetadata'], $metadata); // Test nested object in short notation metadata @@ -401,25 +384,25 @@ public function testGetObjectPropertiesMetadataWithValidClasses() $this->assertEquals($this->expectedProductMetadata['propertiesMetadata']['category']['propertiesMetadata']['tags']['propertiesMetadata'], $metadata); // Test non-existing bundle - $this->assertThrows(\UnexpectedValueException::class, function () { + $this->assertThrows(\UnexpectedValueException::class, function (): void { $this->metadataCollector->getObjectPropertiesMetadata('NonExistingBundle:Test'); }); // Test non-existing class - $this->assertThrows(\ReflectionException::class, function () { + $this->assertThrows(\ReflectionException::class, function (): void { $this->metadataCollector->getObjectPropertiesMetadata('Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\NonExisting'); }); } - public function testGetDocumentClassIndex() + public function testGetDocumentClassIndex(): void { $docClassIndex = $this->metadataCollector->getDocumentClassIndex('AcmeBarBundle:Product'); $this->assertEquals('bar', $docClassIndex); - $docClassIndex = $this->metadataCollector->getDocumentClassIndex('Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Product'); + $docClassIndex = $this->metadataCollector->getDocumentClassIndex(Product::class); $this->assertEquals('bar', $docClassIndex); - $this->assertThrows(\InvalidArgumentException::class, function () { + $this->assertThrows(\InvalidArgumentException::class, function (): void { $this->metadataCollector->getDocumentClassIndex('Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\FooBundle\Document\NonExistingClass'); }); } diff --git a/tests/Functional/Mapping/DocumentParserTest.php b/tests/Functional/Mapping/DocumentParserTest.php index 9b92637..f028edf 100644 --- a/tests/Functional/Mapping/DocumentParserTest.php +++ b/tests/Functional/Mapping/DocumentParserTest.php @@ -6,16 +6,14 @@ use Sineflow\ElasticsearchBundle\Mapping\DocumentLocator; use Sineflow\ElasticsearchBundle\Mapping\DocumentParser; use Sineflow\ElasticsearchBundle\Tests\AbstractContainerAwareTestCase; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\ObjCategory; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\ObjTag; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Product; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Repository\ProductRepository; -/** - * Class DocumentParserTest - */ class DocumentParserTest extends AbstractContainerAwareTestCase { - /** - * @var DocumentParser - */ - private $documentParser; + private DocumentParser $documentParser; protected function setUp(): void { @@ -26,282 +24,282 @@ protected function setUp(): void $this->documentParser = new DocumentParser($reader, $locator, $separator, $languages); } - public function testParseNonDocument() + public function testParseNonDocument(): void { - $reflection = new \ReflectionClass('Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\ObjCategory'); + $reflection = new \ReflectionClass(ObjCategory::class); $res = $this->documentParser->parse($reflection, []); $this->assertEquals([], $res); } - public function testParse() + public function testParse(): void { - $reflection = new \ReflectionClass('Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Product'); + $reflection = new \ReflectionClass(Product::class); $indexAnalyzers = [ 'default_analyzer' => [ - 'type' => 'standard', - ], + 'type' => 'standard', + ], 'en_analyzer' => [ - 'type' => 'standard', - ], + 'type' => 'standard', + ], ]; $res = $this->documentParser->parse($reflection, $indexAnalyzers); $expected = [ 'properties' => [ - 'title' => [ - 'fields' => [ - 'raw' => [ - 'type' => 'keyword', - ], - 'title' => [ - 'type' => 'text', - ], - ], + 'title' => [ + 'fields' => [ + 'raw' => [ + 'type' => 'keyword', + ], + 'title' => [ 'type' => 'text', + ], ], - 'description' => [ - 'type' => 'text', - ], - 'category' => [ - 'properties' => [ - 'id' => [ - 'type' => 'integer', - ], - 'title' => [ - 'type' => 'keyword', - ], - 'tags' => [ - 'properties' => [ - 'tagname' => [ - 'type' => 'text', - ], - ], - ], - ], - ], - 'related_categories' => [ - 'properties' => [ - 'id' => [ - 'type' => 'integer', - ], - 'title' => [ - 'type' => 'keyword', - ], - 'tags' => [ - 'properties' => [ - 'tagname' => [ - 'type' => 'text', - ], - ], - ], - ], - ], - 'price' => [ - 'type' => 'float', - ], - 'location' => [ - 'type' => 'geo_point', - ], - 'limited' => [ - 'type' => 'boolean', - ], - 'released' => [ - 'type' => 'date', - ], - 'ml_info-en' => [ - 'analyzer' => 'en_analyzer', - 'fields' => [ - 'ngram' => [ - 'type' => 'text', - 'analyzer' => 'en_analyzer', - ], + 'type' => 'text', + ], + 'description' => [ + 'type' => 'text', + ], + 'category' => [ + 'properties' => [ + 'id' => [ + 'type' => 'integer', + ], + 'title' => [ + 'type' => 'keyword', + ], + 'tags' => [ + 'properties' => [ + 'tagname' => [ + 'type' => 'text', + ], ], - 'type' => 'text', + ], ], - 'ml_info-fr' => [ - 'analyzer' => 'default_analyzer', - 'fields' => [ - 'ngram' => [ - 'type' => 'text', - 'analyzer' => 'default_analyzer', - ], + ], + 'related_categories' => [ + 'properties' => [ + 'id' => [ + 'type' => 'integer', + ], + 'title' => [ + 'type' => 'keyword', + ], + 'tags' => [ + 'properties' => [ + 'tagname' => [ + 'type' => 'text', + ], ], - 'type' => 'text', - ], - 'ml_info-default' => [ - 'type' => 'keyword', - 'ignore_above' => 256, - ], - 'ml_more_info-en' => [ - 'type' => 'text', - ], - 'ml_more_info-fr' => [ - 'type' => 'text', + ], ], - 'ml_more_info-default' => [ - 'type' => 'text', - 'index' => false, + ], + 'price' => [ + 'type' => 'float', + ], + 'location' => [ + 'type' => 'geo_point', + ], + 'limited' => [ + 'type' => 'boolean', + ], + 'released' => [ + 'type' => 'date', + ], + 'ml_info-en' => [ + 'analyzer' => 'en_analyzer', + 'fields' => [ + 'ngram' => [ + 'type' => 'text', + 'analyzer' => 'en_analyzer', + ], ], - 'pieces_count' => [ - 'fields' => [ - 'count' => [ - 'type' => 'token_count', - 'analyzer' => 'whitespace', - ], - ], - 'type' => 'text', + 'type' => 'text', + ], + 'ml_info-fr' => [ + 'analyzer' => 'default_analyzer', + 'fields' => [ + 'ngram' => [ + 'type' => 'text', + 'analyzer' => 'default_analyzer', + ], ], + 'type' => 'text', ], - 'fields' => [ - 'dynamic' => 'strict', + 'ml_info-default' => [ + 'type' => 'keyword', + 'ignore_above' => 256, ], - 'propertiesMetadata' => [ - 'title' => [ - 'propertyName' => 'title', - 'type' => 'text', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'description' => [ - 'propertyName' => 'description', - 'type' => 'text', - 'multilanguage' => null, - 'propertyAccess' => 1, + 'ml_more_info-en' => [ + 'type' => 'text', + ], + 'ml_more_info-fr' => [ + 'type' => 'text', + ], + 'ml_more_info-default' => [ + 'type' => 'text', + 'index' => false, + ], + 'pieces_count' => [ + 'fields' => [ + 'count' => [ + 'type' => 'token_count', + 'analyzer' => 'whitespace', + ], ], - 'category' => [ - 'propertyName' => 'category', - 'type' => 'object', - 'multilanguage' => null, - 'multiple' => null, - 'propertiesMetadata' => [ - 'id' => [ - 'propertyName' => 'id', - 'type' => 'integer', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'title' => [ - 'propertyName' => 'title', - 'type' => 'keyword', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'tags' => [ - 'propertyName' => 'tags', - 'type' => 'object', - 'multilanguage' => null, - 'multiple' => true, - 'propertiesMetadata' => [ - 'tagname' => [ - 'propertyName' => 'tagName', - 'type' => 'text', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - ], - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\ObjTag', - 'propertyAccess' => 1, - ], - ], - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\ObjCategory', + 'type' => 'text', + ], + ], + 'fields' => [ + 'dynamic' => 'strict', + ], + 'propertiesMetadata' => [ + 'title' => [ + 'propertyName' => 'title', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'description' => [ + 'propertyName' => 'description', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'category' => [ + 'propertyName' => 'category', + 'type' => 'object', + 'multilanguage' => null, + 'multiple' => null, + 'propertiesMetadata' => [ + 'id' => [ + 'propertyName' => 'id', + 'type' => 'integer', + 'multilanguage' => null, 'propertyAccess' => 1, - ], - 'related_categories' => [ - 'propertyName' => 'relatedCategories', - 'type' => 'object', - 'multilanguage' => null, - 'multiple' => true, - 'propertiesMetadata' => [ - 'id' => [ - 'propertyName' => 'id', - 'type' => 'integer', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'title' => [ - 'propertyName' => 'title', - 'type' => 'keyword', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'tags' => [ - 'propertyName' => 'tags', - 'type' => 'object', - 'multilanguage' => null, - 'multiple' => true, - 'propertiesMetadata' => [ - 'tagname' => [ - 'propertyName' => 'tagName', - 'type' => 'text', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - ], - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\ObjTag', - 'propertyAccess' => 1, - ], + ], + 'title' => [ + 'propertyName' => 'title', + 'type' => 'keyword', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'tags' => [ + 'propertyName' => 'tags', + 'type' => 'object', + 'multilanguage' => null, + 'multiple' => true, + 'propertiesMetadata' => [ + 'tagname' => [ + 'propertyName' => 'tagName', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], ], - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\ObjCategory', + 'className' => ObjTag::class, 'propertyAccess' => 1, + ], ], - 'price' => [ - 'propertyName' => 'price', - 'type' => 'float', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'location' => [ - 'propertyName' => 'location', - 'type' => 'geo_point', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'limited' => [ - 'propertyName' => 'limited', - 'type' => 'boolean', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'released' => [ - 'propertyName' => 'released', - 'type' => 'date', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - 'ml_info' => [ - 'propertyName' => 'mlInfo', - 'type' => 'text', - 'multilanguage' => true, + 'className' => ObjCategory::class, + 'propertyAccess' => 1, + ], + 'related_categories' => [ + 'propertyName' => 'relatedCategories', + 'type' => 'object', + 'multilanguage' => null, + 'multiple' => true, + 'propertiesMetadata' => [ + 'id' => [ + 'propertyName' => 'id', + 'type' => 'integer', + 'multilanguage' => null, 'propertyAccess' => 1, ], - 'ml_more_info' => [ - 'propertyName' => 'mlMoreInfo', - 'type' => 'text', - 'multilanguage' => true, + 'title' => [ + 'propertyName' => 'title', + 'type' => 'keyword', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'tags' => [ + 'propertyName' => 'tags', + 'type' => 'object', + 'multilanguage' => null, + 'multiple' => true, + 'propertiesMetadata' => [ + 'tagname' => [ + 'propertyName' => 'tagName', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + ], + 'className' => ObjTag::class, 'propertyAccess' => 1, ], - 'pieces_count' => [ - 'propertyName' => 'tokenPiecesCount', - 'type' => 'text', - 'multilanguage' => null, - 'propertyAccess' => 1, - ], - '_id' => [ - 'propertyName' => 'id', - 'type' => 'keyword', - 'propertyAccess' => 1, - ], - '_score' => [ - 'propertyName' => 'score', - 'type' => 'float', - 'propertyAccess' => 1, ], + 'className' => ObjCategory::class, + 'propertyAccess' => 1, + ], + 'price' => [ + 'propertyName' => 'price', + 'type' => 'float', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'location' => [ + 'propertyName' => 'location', + 'type' => 'geo_point', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'limited' => [ + 'propertyName' => 'limited', + 'type' => 'boolean', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'released' => [ + 'propertyName' => 'released', + 'type' => 'date', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + 'ml_info' => [ + 'propertyName' => 'mlInfo', + 'type' => 'text', + 'multilanguage' => true, + 'propertyAccess' => 1, + ], + 'ml_more_info' => [ + 'propertyName' => 'mlMoreInfo', + 'type' => 'text', + 'multilanguage' => true, + 'propertyAccess' => 1, + ], + 'pieces_count' => [ + 'propertyName' => 'tokenPiecesCount', + 'type' => 'text', + 'multilanguage' => null, + 'propertyAccess' => 1, + ], + '_id' => [ + 'propertyName' => 'id', + 'type' => 'keyword', + 'propertyAccess' => 1, + ], + '_score' => [ + 'propertyName' => 'score', + 'type' => 'float', + 'propertyAccess' => 1, ], - 'repositoryClass' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\Repository\\ProductRepository', - 'providerClass' => null, - 'className' => 'Sineflow\\ElasticsearchBundle\\Tests\\App\\Fixture\\Acme\\BarBundle\\Document\\Product', + ], + 'repositoryClass' => ProductRepository::class, + 'providerClass' => null, + 'className' => Product::class, ]; $this->assertEquals($expected, $res); diff --git a/tests/Functional/Profiler/ElasticsearchProfilerTest.php b/tests/Functional/Profiler/ElasticsearchProfilerTest.php index 4b6fdd2..a415e62 100644 --- a/tests/Functional/Profiler/ElasticsearchProfilerTest.php +++ b/tests/Functional/Profiler/ElasticsearchProfilerTest.php @@ -41,7 +41,7 @@ protected function getDataArray() /** * Tests if right amount of queries are caught */ - public function testGetQueryCount() + public function testGetQueryCount(): void { $imWithoutAliases = $this->getIndexManager('bar'); $imWithoutAliases->getConnection()->setAutocommit(false); @@ -67,7 +67,7 @@ public function testGetQueryCount() /** * Tests if correct time is being returned. */ - public function testGetTime() + public function testGetTime(): void { $imWithoutAliases = $this->getIndexManager('bar'); $imWithoutAliases->getConnection()->setAutocommit(true); @@ -80,7 +80,7 @@ public function testGetTime() /** * Tests if logged data seems correct */ - public function testCorrectDataLogged() + public function testCorrectDataLogged(): void { $imWithoutAliases = $this->getIndexManager('bar'); $imWithoutAliases->getConnection()->setAutocommit(true); @@ -91,12 +91,12 @@ public function testCorrectDataLogged() $lastQuery = \end($queries[ElasticsearchProfiler::UNDEFINED_ROUTE]); $this->checkQueryParameters($lastQuery); - $esHostAndPort = \explode(':', $imWithoutAliases->getConnection()->getConnectionSettings()['hosts'][0]); + $esHostAndPort = \explode(':', (string) $imWithoutAliases->getConnection()->getConnectionSettings()['hosts'][0]); $this->assertArraySubset( [ - 'curlRequest' => "curl -XPOST 'http://".\implode(':', $esHostAndPort)."/sineflow-esb-test-bar/_search?pretty=true' -d '{\"query\":{\"ids\":{\"values\":[\"3\"]}},\"version\":true}'", - 'senseRequest' => "POST /sineflow-esb-test-bar/_search\n{\"query\":{\"ids\":{\"values\":[\"3\"]}},\"version\":true}", + 'curlRequest' => "curl -XPOST 'http://".\implode(':', $esHostAndPort)."/sineflow-esb-test-bar/_search?pretty=true' -d '{\"query\":{\"ids\":{\"values\":[3]}},\"version\":true}'", + 'senseRequest' => "POST /sineflow-esb-test-bar/_search\n{\"query\":{\"ids\":{\"values\":[3]}},\"version\":true}", 'backtrace' => null, 'scheme' => 'http', 'host' => $esHostAndPort[0], diff --git a/tests/Functional/Result/DocumentConverterTest.php b/tests/Functional/Result/DocumentConverterTest.php index cd37378..e501231 100644 --- a/tests/Functional/Result/DocumentConverterTest.php +++ b/tests/Functional/Result/DocumentConverterTest.php @@ -16,7 +16,7 @@ class DocumentConverterTest extends AbstractContainerAwareTestCase { use ArraySubsetAsserts; - private $fullDocArray = [ + private array $fullDocArray = [ '_id' => 'doc1', 'title' => 'Foo Product', 'category' => [ @@ -33,9 +33,9 @@ class DocumentConverterTest extends AbstractContainerAwareTestCase 'nonexisting' => 'should be skipped', ]; - public function testAssignArrayToObjectWithNestedSingleValueInsteadOfArray() + public function testAssignArrayToObjectWithNestedSingleValueInsteadOfArray(): void { - $converter = $this->getContainer()->get('Sineflow\ElasticsearchBundle\Result\DocumentConverter'); + $converter = $this->getContainer()->get(DocumentConverter::class); $metadataCollector = $this->getContainer()->get(DocumentMetadataCollector::class); // Raw doc with a single object value where an array of objects is expected according to metadata def @@ -58,7 +58,7 @@ public function testAssignArrayToObjectWithNestedSingleValueInsteadOfArray() $this->assertEquals($category->id, 123); } - public function testAssignArrayToObjectWithNestedSingleValueArrayInsteadOfSingleValue() + public function testAssignArrayToObjectWithNestedSingleValueArrayInsteadOfSingleValue(): void { $converter = $this->getContainer()->get(DocumentConverter::class); $metadataCollector = $this->getContainer()->get(DocumentMetadataCollector::class); @@ -84,7 +84,7 @@ public function testAssignArrayToObjectWithNestedSingleValueArrayInsteadOfSingle $this->assertEquals($result->category->id, 123); } - public function testAssignArrayToObjectWithNestedMultiValueArrayInsteadOfSingleValue() + public function testAssignArrayToObjectWithNestedMultiValueArrayInsteadOfSingleValue(): void { $converter = $this->getContainer()->get(DocumentConverter::class); $metadataCollector = $this->getContainer()->get(DocumentMetadataCollector::class); @@ -143,7 +143,7 @@ public function testAssignArrayToObjectWithAllFieldsCorrectlySet() return $product; } - public function testAssignArrayToObjectWithEmptyFields() + public function testAssignArrayToObjectWithEmptyFields(): void { $converter = $this->getContainer()->get(DocumentConverter::class); $metadataCollector = $this->getContainer()->get(DocumentMetadataCollector::class); @@ -162,7 +162,7 @@ public function testAssignArrayToObjectWithEmptyFields() $this->assertNull($product->mlInfo); } - public function testAssignArrayToObjectWithEmptyMultipleNestedField() + public function testAssignArrayToObjectWithEmptyMultipleNestedField(): void { $converter = $this->getContainer()->get(DocumentConverter::class); $metadataCollector = $this->getContainer()->get(DocumentMetadataCollector::class); @@ -184,9 +184,9 @@ public function testAssignArrayToObjectWithEmptyMultipleNestedField() /** * @depends testAssignArrayToObjectWithAllFieldsCorrectlySet */ - public function testConvertToArray(Product $product) + public function testConvertToArray(Product $product): void { - $converter = $this->getContainer()->get('Sineflow\ElasticsearchBundle\Result\DocumentConverter'); + $converter = $this->getContainer()->get(DocumentConverter::class); $arr = $converter->convertToArray($product); @@ -194,7 +194,7 @@ public function testConvertToArray(Product $product) $this->assertArraySubset($arr, $this->fullDocArray); } - public function testConvertToDocumentWithSource() + public function testConvertToDocumentWithSource(): void { $rawFromEs = [ '_index' => 'sineflow-esb-test-bar', @@ -202,21 +202,21 @@ public function testConvertToDocumentWithSource() '_version' => 1, 'found' => true, '_source' => [ - 'title' => 'Foo Product', - 'category' => [ - 'title' => 'Bar', - ], - 'related_categories' => [ - 0 => [ - 'title' => 'Acme', - ], - ], - 'ml_info-en' => 'info in English', - 'ml_info-fr' => 'info in French', + 'title' => 'Foo Product', + 'category' => [ + 'title' => 'Bar', + ], + 'related_categories' => [ + 0 => [ + 'title' => 'Acme', + ], ], + 'ml_info-en' => 'info in English', + 'ml_info-fr' => 'info in French', + ], ]; - $converter = $this->getContainer()->get('Sineflow\ElasticsearchBundle\Result\DocumentConverter'); + $converter = $this->getContainer()->get(DocumentConverter::class); /** @var Product $product */ $product = $converter->convertToDocument($rawFromEs, 'AcmeBarBundle:Product'); @@ -230,27 +230,27 @@ public function testConvertToDocumentWithSource() $this->assertEquals('info in French', $product->mlInfo->getValue('fr')); } - public function testConvertToDocumentWithFields() + public function testConvertToDocumentWithFields(): void { $rawFromEs = [ '_index' => 'sineflow-esb-test-bar', '_id' => 'doc1', '_score' => 1, 'fields' => [ - 'title' => [ - 0 => 'Foo Product', - ], - 'related_categories.title' => [ - 0 => 'Acme', - 1 => 'Bar', - ], - 'category.title' => [ - 0 => 'Bar', - ], - 'ml_info-en' => [ - 0 => 'info in English', - ], + 'title' => [ + 0 => 'Foo Product', ], + 'related_categories.title' => [ + 0 => 'Acme', + 1 => 'Bar', + ], + 'category.title' => [ + 0 => 'Bar', + ], + 'ml_info-en' => [ + 0 => 'info in English', + ], + ], ]; /** @var DocumentConverter $converter */ diff --git a/tests/Functional/Result/DocumentIteratorTest.php b/tests/Functional/Result/DocumentIteratorTest.php index 3405d08..074fe1c 100644 --- a/tests/Functional/Result/DocumentIteratorTest.php +++ b/tests/Functional/Result/DocumentIteratorTest.php @@ -74,7 +74,7 @@ protected function getDataArray() /** * Iteration test. */ - public function testIteration() + public function testIteration(): void { /** @var Repository $repo */ $repo = $this->getIndexManager('bar')->getRepository(); @@ -116,7 +116,7 @@ public function testIteration() /** * Manual iteration test. */ - public function testManualIteration() + public function testManualIteration(): void { /** @var Repository $repo */ $repo = $this->getIndexManager('bar')->getRepository(); @@ -146,7 +146,7 @@ public function testManualIteration() /** * Tests if current() returns null when data doesn't exist. */ - public function testCurrentWithEmptyIterator() + public function testCurrentWithEmptyIterator(): void { /** @var Repository $repo */ $repo = $this->getIndexManager('customer')->getRepository(); @@ -159,7 +159,7 @@ public function testCurrentWithEmptyIterator() /** * Make sure null is returned when field doesn't exist or is empty and ObjectIterator otherwise */ - public function testNestedObjectIterator() + public function testNestedObjectIterator(): void { /** @var Repository $repo */ $repo = $this->getIndexManager('bar')->getRepository(); @@ -189,7 +189,7 @@ public function testNestedObjectIterator() /** * Test that aggregations are returned */ - public function testAggregations() + public function testAggregations(): void { /** @var Repository $repo */ $repo = $this->getIndexManager('bar')->getRepository(); @@ -214,7 +214,7 @@ public function testAggregations() /** * Test that suggestions are returned */ - public function testSuggestions() + public function testSuggestions(): void { /** @var Repository $repo */ $repo = $this->getIndexManager('bar')->getRepository(); diff --git a/tests/Functional/Subscriber/EntityTrackerSubscriberTest.php b/tests/Functional/Subscriber/EntityTrackerSubscriberTest.php index 584fd97..9b658bd 100644 --- a/tests/Functional/Subscriber/EntityTrackerSubscriberTest.php +++ b/tests/Functional/Subscriber/EntityTrackerSubscriberTest.php @@ -18,7 +18,7 @@ class EntityTrackerSubscriberTest extends AbstractElasticsearchTestCase * Test executing 2 separate bulk requests, where there's an error in the first one, * to make sure the subscriber properly keeps track of bulk request items. */ - public function testTwoBulkRequestWithErrorInFirstOne() + public function testTwoBulkRequestWithErrorInFirstOne(): void { $imWithAliases = $this->getIndexManager('customer'); $customer1 = new Customer(); @@ -30,7 +30,7 @@ public function testTwoBulkRequestWithErrorInFirstOne() $imWithAliases->persist($customer2); try { $imWithAliases->getConnection()->commit(); - } catch (BulkRequestException $e) { + } catch (BulkRequestException) { // ignore the exception } @@ -61,7 +61,7 @@ public function testTwoBulkRequestWithErrorInFirstOne() /** * Test populating persisted entity ids after a bulk operation with several operations */ - public function testPersistWithSeveralBulkOps() + public function testPersistWithSeveralBulkOps(): void { $converter = $this->getContainer()->get(DocumentConverter::class); diff --git a/tests/Unit/Annotation/DocumentTest.php b/tests/Unit/Annotation/DocumentTest.php index e1cca77..48152f4 100644 --- a/tests/Unit/Annotation/DocumentTest.php +++ b/tests/Unit/Annotation/DocumentTest.php @@ -13,7 +13,7 @@ class DocumentTest extends TestCase /** * Tests if values are dumped correctly */ - public function testDump() + public function testDump(): void { $doc = new Document(); diff --git a/tests/Unit/Annotation/PropertyTest.php b/tests/Unit/Annotation/PropertyTest.php index 5cbe007..35bed20 100644 --- a/tests/Unit/Annotation/PropertyTest.php +++ b/tests/Unit/Annotation/PropertyTest.php @@ -13,7 +13,7 @@ class PropertyTest extends TestCase /** * Tests if values are dumped correctly for mapping. */ - public function testDump() + public function testDump(): void { $type = new Property(); @@ -21,7 +21,7 @@ public function testDump() $type->type = 'mytype'; $type->multilanguage = false; $type->objectName = 'foo/bar'; - $type->multiple = null; + $type->multiple = false; $type->options = [ 'type' => 'this should not be set here', 'analyzer' => 'standard', @@ -43,7 +43,7 @@ public function testDump() /** * Test if language placeholders are correctly replaced */ - public function testDumpML() + public function testDumpML(): void { $type = new Property(); @@ -51,7 +51,7 @@ public function testDumpML() $type->type = 'mytype'; $type->multilanguage = true; $type->objectName = 'foo/bar'; - $type->multiple = null; + $type->multiple = false; $type->options = [ 'copy_to' => '{lang}_all', 'analyzer' => '{lang}_analyzer', @@ -79,10 +79,10 @@ public function testDumpML() 'copy_to' => 'en_all', 'analyzer' => 'en_analyzer', 'fields' => [ - 'ngram' => [ - 'analyzer' => 'en_analyzer', - ], + 'ngram' => [ + 'analyzer' => 'en_analyzer', ], + ], 'type' => 'mytype', ], $type->dump($settings), @@ -93,7 +93,7 @@ public function testDumpML() /** * Test that exception is thrown when language is specified but there are no index analyzers set */ - public function testDumpNoAnalyzersException() + public function testDumpNoAnalyzersException(): void { $type = new Property(); @@ -101,7 +101,7 @@ public function testDumpNoAnalyzersException() $type->type = 'mytype'; $type->multilanguage = false; $type->objectName = 'foo/bar'; - $type->multiple = null; + $type->multiple = false; $type->options = [ 'analyzer' => '{lang}_analyzer', ]; @@ -117,7 +117,7 @@ public function testDumpNoAnalyzersException() /** * Test that exception is thrown when no default language analyzer is set */ - public function testDumpNoDefaultException() + public function testDumpNoDefaultException(): void { $type = new Property(); @@ -125,7 +125,7 @@ public function testDumpNoDefaultException() $type->type = 'mytype'; $type->multilanguage = false; $type->objectName = 'foo/bar'; - $type->multiple = null; + $type->multiple = false; $type->options = [ 'analyzer' => '{lang}_analyzer', ]; diff --git a/tests/Unit/DTO/BulkQueryItemTest.php b/tests/Unit/DTO/BulkQueryItemTest.php index f546a4b..03f0161 100644 --- a/tests/Unit/DTO/BulkQueryItemTest.php +++ b/tests/Unit/DTO/BulkQueryItemTest.php @@ -76,7 +76,7 @@ public function getLinesProvider() * * @dataProvider getLinesProvider */ - public function testGetLines($input, $expected) + public function testGetLines($input, $expected): void { $bqi = new BulkQueryItem($input[0], $input[1], $input[2]); $lines = $bqi->getLines($input[3]); diff --git a/tests/Unit/DTO/IndicesToDocumentClassesTest.php b/tests/Unit/DTO/IndicesToDocumentClassesTest.php index b2f60dd..d8f56c6 100644 --- a/tests/Unit/DTO/IndicesToDocumentClassesTest.php +++ b/tests/Unit/DTO/IndicesToDocumentClassesTest.php @@ -13,18 +13,18 @@ class IndicesToDocumentClassesTest extends TestCase { use AssertThrows; - public function testGetSet() + public function testGetSet(): void { $obj = new IndicesToDocumentClasses(); $obj->set('my_real_index', 'App:Entity'); $this->assertEquals('App:Entity', $obj->get('my_real_index')); - $this->assertThrows(\InvalidArgumentException::class, static function () use ($obj) { + $this->assertThrows(\InvalidArgumentException::class, static function () use ($obj): void { $obj->set(null, 'App:Entity'); }); - $this->assertThrows(\InvalidArgumentException::class, static function () use ($obj) { + $this->assertThrows(\InvalidArgumentException::class, static function () use ($obj): void { $obj->get('non_existing_index'); }); @@ -34,7 +34,7 @@ public function testGetSet() $this->assertEquals('App:Entity', $obj->get('second_real_index')); $this->assertEquals('App:Entity', $obj->get('non_existing_index')); - $this->assertThrows(\InvalidArgumentException::class, static function () use ($obj) { + $this->assertThrows(\InvalidArgumentException::class, static function () use ($obj): void { $obj->set('my_real_index', 'App:Entity'); }); } diff --git a/tests/Unit/DependencyInjection/Compiler/AddIndexManagersPassTest.php b/tests/Unit/DependencyInjection/Compiler/AddIndexManagersPassTest.php index d380231..3aaa2ba 100644 --- a/tests/Unit/DependencyInjection/Compiler/AddIndexManagersPassTest.php +++ b/tests/Unit/DependencyInjection/Compiler/AddIndexManagersPassTest.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase; use Sineflow\ElasticsearchBundle\DependencyInjection\Compiler\AddIndexManagersPass; use Sineflow\ElasticsearchBundle\Manager\IndexManager; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; /** @@ -15,7 +16,7 @@ class AddIndexManagersPassTest extends TestCase /** * Before a test method is run, a template method called setUp() is invoked. */ - public function testProcessWithSeveralManagers() + public function testProcessWithSeveralManagers(): void { $connections = [ 'test1' => [ @@ -39,33 +40,24 @@ public function testProcessWithSeveralManagers() ], ]; - $containerMock = $this->getMockBuilder('\Symfony\Component\DependencyInjection\ContainerBuilder') + $containerMock = $this->getMockBuilder(ContainerBuilder::class) ->disableOriginalConstructor() ->getMock(); $containerMock->method('hasDefinition')->with($this->anything()) ->willReturnCallback( - static function ($parameter) { - switch ($parameter) { - case 'sfes.connection.test1': - return true; - default: - return null; - } + static fn ($parameter): ?bool => match ($parameter) { + 'sfes.connection.test1' => true, + default => null, } ); $containerMock->expects($this->exactly(1))->method('getParameter')->with($this->anything()) ->willReturnCallback( - static function ($parameter) use ($connections, $managers) { - switch ($parameter) { - case 'sfes.indices': - return $managers; - case 'sfes.connections': - return $connections; - default: - return null; - } + static fn ($parameter): ?array => match ($parameter) { + 'sfes.indices' => $managers, + 'sfes.connections' => $connections, + default => null, } ); diff --git a/tests/Unit/DependencyInjection/ElasticsearchExtensionTest.php b/tests/Unit/DependencyInjection/ElasticsearchExtensionTest.php index c8cb67e..d5bd3b3 100644 --- a/tests/Unit/DependencyInjection/ElasticsearchExtensionTest.php +++ b/tests/Unit/DependencyInjection/ElasticsearchExtensionTest.php @@ -167,7 +167,7 @@ public function getData() * * @dataProvider getData */ - public function testLoad($parameters, $expectedEntityLocations, $expectedConnections, $expectedManagers) + public function testLoad($parameters, $expectedEntityLocations, $expectedConnections, $expectedManagers): void { $container = new ContainerBuilder(); \class_exists('testClass') ?: eval('class testClass {}'); diff --git a/tests/Unit/Document/MLPropertyTest.php b/tests/Unit/Document/MLPropertyTest.php index 8a4d384..96010dc 100644 --- a/tests/Unit/Document/MLPropertyTest.php +++ b/tests/Unit/Document/MLPropertyTest.php @@ -13,7 +13,7 @@ class MLPropertyTest extends TestCase /** * Tests if value is set and returned correctly */ - public function testGetSetValue() + public function testGetSetValue(): void { $mlProperty = new MLProperty(); $mlProperty->setValue('test en', 'en'); @@ -47,7 +47,7 @@ public function testGetSetValue() /** * Tests if returns all values */ - public function testGetValues() + public function testGetValues(): void { $mlProperty = new MLProperty(); $mlProperty->setValue('test default', 'default'); @@ -68,7 +68,7 @@ public function testGetValues() /** * Tests if construct set all values properly */ - public function testConstruct() + public function testConstruct(): void { $mlProperty = new MLProperty([ 'default' => 'test default', diff --git a/tests/Unit/ElasticsearchBundleTest.php b/tests/Unit/ElasticsearchBundleTest.php index 5182287..3f81398 100644 --- a/tests/Unit/ElasticsearchBundleTest.php +++ b/tests/Unit/ElasticsearchBundleTest.php @@ -22,7 +22,7 @@ class ElasticsearchBundleTest extends TestCase /** * Check whether all Passes in DependencyInjection/Compiler/ are added to container. */ - public function testPassesRegistered() + public function testPassesRegistered(): void { $container = new ContainerBuilder(); $bundle = new SineflowElasticsearchBundle(); @@ -33,7 +33,7 @@ public function testPassesRegistered() /** @var PassConfig $passConfig */ $passConfig = $container->getCompiler()->getPassConfig(); foreach ($passConfig->getPasses() as $pass) { - $classPath = \explode('\\', \get_class($pass)); + $classPath = \explode('\\', $pass::class); $loadedPasses[] = \end($classPath); } diff --git a/tests/Unit/Mapping/CaserTest.php b/tests/Unit/Mapping/CaserTest.php index 840942e..c723179 100644 --- a/tests/Unit/Mapping/CaserTest.php +++ b/tests/Unit/Mapping/CaserTest.php @@ -7,7 +7,7 @@ class CaserTest extends TestCase { - public function providerForCamel() + public function providerForCamel(): array { $out = [ ['foo_bar', 'fooBar'], @@ -20,7 +20,7 @@ public function providerForCamel() return $out; } - public function providerForSnake() + public function providerForSnake(): array { $out = [ ['FooBar', 'foo_bar'], @@ -38,7 +38,7 @@ public function providerForSnake() * * @dataProvider providerForCamel */ - public function testCamel($input, $expected) + public function testCamel($input, $expected): void { $this->assertEquals($expected, Caser::camel($input)); } @@ -49,7 +49,7 @@ public function testCamel($input, $expected) * * @dataProvider providerForSnake */ - public function testSnake($input, $expected) + public function testSnake($input, $expected): void { $this->assertEquals($expected, Caser::snake($input)); } diff --git a/tests/Unit/Mapping/DocumentLocatorTest.php b/tests/Unit/Mapping/DocumentLocatorTest.php index ad97091..5e5ee18 100644 --- a/tests/Unit/Mapping/DocumentLocatorTest.php +++ b/tests/Unit/Mapping/DocumentLocatorTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; use Sineflow\ElasticsearchBundle\Mapping\DocumentLocator; +use Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Product; /** * Class DocumentLocatorTest @@ -31,23 +32,21 @@ protected function setUp(): void /** * Data provider - * - * @return array */ - public function getTestResolveClassNameDataProvider() + public function getTestResolveClassNameDataProvider(): array { $out = [ [ 'AcmeBarBundle:Product', - 'Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Product', + Product::class, ], [ 'AcmeFooBundle:Product', 'Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\FooBundle\Document\Product', ], [ - 'Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Product', - 'Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Product', + Product::class, + Product::class, ], ]; @@ -56,14 +55,12 @@ public function getTestResolveClassNameDataProvider() /** * Data provider - * - * @return array */ - public function getShortClassNameDataProvider() + public function getShortClassNameDataProvider(): array { $out = [ [ - 'Sineflow\ElasticsearchBundle\Tests\App\Fixture\Acme\BarBundle\Document\Product', + Product::class, 'AcmeBarBundle:Product', ], [ @@ -81,10 +78,8 @@ public function getShortClassNameDataProvider() /** * Data provider - * - * @return array */ - public function getShortClassNameExceptionsDataProvider() + public function getShortClassNameExceptionsDataProvider(): array { $out = [ [ @@ -104,7 +99,7 @@ public function getShortClassNameExceptionsDataProvider() /** * Tests getAllDocumentDirs */ - public function testGetAllDocumentDirs() + public function testGetAllDocumentDirs(): void { $this->assertCount(2, $this->locator->getAllDocumentDirs()); } @@ -117,7 +112,7 @@ public function testGetAllDocumentDirs() * * @dataProvider getTestResolveClassNameDataProvider */ - public function testResolveClassName($className, $expectedClassName) + public function testResolveClassName($className, $expectedClassName): void { $this->assertEquals($expectedClassName, $this->locator->resolveClassName($className)); }