├── .github ├── ISSUE_TEMPLATE │ └── Bug_report.yml └── workflows │ ├── do-not-edit-this-repository.yml │ └── issue.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── lib ├── Algolia.php ├── Api │ ├── AbtestingClient.php │ ├── AnalyticsClient.php │ ├── CompositionClient.php │ ├── IngestionClient.php │ ├── InsightsClient.php │ ├── MonitoringClient.php │ ├── PersonalizationClient.php │ ├── QuerySuggestionsClient.php │ ├── RecommendClient.php │ └── SearchClient.php ├── ApiException.php ├── Cache │ └── NullCacheDriver.php ├── Configuration │ ├── AbtestingConfig.php │ ├── AnalyticsConfig.php │ ├── CompositionConfig.php │ ├── ConfigWithRegion.php │ ├── Configuration.php │ ├── IngestionConfig.php │ ├── InsightsConfig.php │ ├── MonitoringConfig.php │ ├── PersonalizationConfig.php │ ├── QuerySuggestionsConfig.php │ ├── RecommendConfig.php │ └── SearchConfig.php ├── Exceptions │ ├── AlgoliaException.php │ ├── BadRequestException.php │ ├── CannotWaitException.php │ ├── ExceededRetriesException.php │ ├── MissingObjectId.php │ ├── NotFoundException.php │ ├── ObjectNotFoundException.php │ ├── RequestException.php │ ├── RetriableException.php │ ├── UnreachableException.php │ └── ValidUntilNotFoundException.php ├── FormDataProcessor.php ├── Http │ ├── CurlHttpClient.php │ ├── GuzzleHttpClient.php │ ├── HttpClientInterface.php │ └── Psr7 │ │ ├── BufferStream.php │ │ ├── PumpStream.php │ │ ├── Request.php │ │ ├── Response.php │ │ ├── Stream.php │ │ ├── Uri.php │ │ ├── UriResolver.php │ │ └── functions.php ├── Iterators │ ├── AbstractAlgoliaIterator.php │ ├── ObjectIterator.php │ ├── RuleIterator.php │ └── SynonymIterator.php ├── Log │ └── DebugLogger.php ├── Model │ ├── AbstractModel.php │ ├── Abtesting │ │ ├── ABTest.php │ │ ├── ABTestConfiguration.php │ │ ├── ABTestResponse.php │ │ ├── AbTestsVariant.php │ │ ├── AbTestsVariantSearchParams.php │ │ ├── AddABTestsRequest.php │ │ ├── AddABTestsVariant.php │ │ ├── Currency.php │ │ ├── EffectMetric.php │ │ ├── EmptySearch.php │ │ ├── EmptySearchFilter.php │ │ ├── ErrorBase.php │ │ ├── EstimateABTestRequest.php │ │ ├── EstimateABTestResponse.php │ │ ├── EstimateConfiguration.php │ │ ├── FilterEffects.php │ │ ├── ListABTestsResponse.php │ │ ├── MinimumDetectableEffect.php │ │ ├── Outliers.php │ │ ├── OutliersFilter.php │ │ ├── ScheduleABTestResponse.php │ │ ├── ScheduleABTestsRequest.php │ │ ├── Status.php │ │ └── Variant.php │ ├── Analytics │ │ ├── ClickPosition.php │ │ ├── CurrencyCode.php │ │ ├── DailyAddToCartRates.php │ │ ├── DailyAverageClicks.php │ │ ├── DailyClickThroughRates.php │ │ ├── DailyConversionRates.php │ │ ├── DailyNoClickRates.php │ │ ├── DailyNoResultsRates.php │ │ ├── DailyPurchaseRates.php │ │ ├── DailyRevenue.php │ │ ├── DailySearches.php │ │ ├── DailySearchesNoClicks.php │ │ ├── DailySearchesNoResults.php │ │ ├── DailyUsers.php │ │ ├── Direction.php │ │ ├── ErrorBase.php │ │ ├── GetAddToCartRateResponse.php │ │ ├── GetAverageClickPositionResponse.php │ │ ├── GetClickPositionsResponse.php │ │ ├── GetClickThroughRateResponse.php │ │ ├── GetConversionRateResponse.php │ │ ├── GetNoClickRateResponse.php │ │ ├── GetNoResultsRateResponse.php │ │ ├── GetPurchaseRateResponse.php │ │ ├── GetRevenue.php │ │ ├── GetSearchesCountResponse.php │ │ ├── GetSearchesNoClicksResponse.php │ │ ├── GetSearchesNoResultsResponse.php │ │ ├── GetStatusResponse.php │ │ ├── GetTopCountriesResponse.php │ │ ├── GetTopFilterAttribute.php │ │ ├── GetTopFilterAttributesResponse.php │ │ ├── GetTopFilterForAttribute.php │ │ ├── GetTopFilterForAttributeResponse.php │ │ ├── GetTopFiltersNoResultsResponse.php │ │ ├── GetTopFiltersNoResultsValue.php │ │ ├── GetTopFiltersNoResultsValues.php │ │ ├── GetTopHitsResponse.php │ │ ├── GetTopSearchesResponse.php │ │ ├── GetUsersCountResponse.php │ │ ├── Operator.php │ │ ├── OrderBy.php │ │ ├── TopCountry.php │ │ ├── TopHit.php │ │ ├── TopHitWithAnalytics.php │ │ ├── TopHitWithRevenueAnalytics.php │ │ ├── TopHitsResponse.php │ │ ├── TopHitsResponseWithAnalytics.php │ │ ├── TopHitsResponseWithRevenueAnalytics.php │ │ ├── TopSearch.php │ │ ├── TopSearchWithAnalytics.php │ │ ├── TopSearchWithRevenueAnalytics.php │ │ ├── TopSearchesResponse.php │ │ ├── TopSearchesResponseWithAnalytics.php │ │ └── TopSearchesResponseWithRevenueAnalytics.php │ ├── Composition │ │ ├── AroundPrecision.php │ │ ├── AroundRadius.php │ │ ├── AroundRadiusAll.php │ │ ├── Banner.php │ │ ├── BannerImage.php │ │ ├── BannerImageUrl.php │ │ ├── BannerLink.php │ │ ├── CompositionIdRankingInfo.php │ │ ├── CompositionRunAppliedRules.php │ │ ├── CompositionRunSearchResponse.php │ │ ├── CompositionsSearchResponse.php │ │ ├── ErrorBase.php │ │ ├── Exhaustive.php │ │ ├── FacetFilters.php │ │ ├── FacetHits.php │ │ ├── FacetOrdering.php │ │ ├── FacetStats.php │ │ ├── Facets.php │ │ ├── HighlightResult.php │ │ ├── HighlightResultOption.php │ │ ├── Hit.php │ │ ├── HitRankingInfo.php │ │ ├── InsideBoundingBox.php │ │ ├── MatchLevel.php │ │ ├── MatchedGeoLocation.php │ │ ├── NumericFilters.php │ │ ├── OptionalFilters.php │ │ ├── Params.php │ │ ├── Personalization.php │ │ ├── Range.php │ │ ├── Redirect.php │ │ ├── RedirectRuleIndexData.php │ │ ├── RedirectRuleIndexMetadata.php │ │ ├── RedirectURL.php │ │ ├── RenderingContent.php │ │ ├── RequestBody.php │ │ ├── ResultsCompositionInfoResponse.php │ │ ├── ResultsInjectedItemAppliedRulesInfoResponse.php │ │ ├── ResultsInjectedItemInfoResponse.php │ │ ├── SearchForFacetValuesParams.php │ │ ├── SearchForFacetValuesRequest.php │ │ ├── SearchForFacetValuesResponse.php │ │ ├── SearchForFacetValuesResults.php │ │ ├── SearchResponse.php │ │ ├── SearchResultsItem.php │ │ ├── SnippetResult.php │ │ ├── SnippetResultOption.php │ │ ├── SortRemainingBy.php │ │ ├── SupportedLanguage.php │ │ ├── Value.php │ │ └── Widgets.php │ ├── Ingestion │ │ ├── Action.php │ │ ├── ActionType.php │ │ ├── AuthAPIKey.php │ │ ├── AuthAPIKeyPartial.php │ │ ├── AuthAlgolia.php │ │ ├── AuthAlgoliaInsights.php │ │ ├── AuthAlgoliaInsightsPartial.php │ │ ├── AuthAlgoliaPartial.php │ │ ├── AuthBasic.php │ │ ├── AuthBasicPartial.php │ │ ├── AuthGoogleServiceAccount.php │ │ ├── AuthGoogleServiceAccountPartial.php │ │ ├── AuthInput.php │ │ ├── AuthInputPartial.php │ │ ├── AuthOAuth.php │ │ ├── AuthOAuthPartial.php │ │ ├── Authentication.php │ │ ├── AuthenticationCreate.php │ │ ├── AuthenticationCreateResponse.php │ │ ├── AuthenticationSearch.php │ │ ├── AuthenticationSortKeys.php │ │ ├── AuthenticationType.php │ │ ├── AuthenticationUpdate.php │ │ ├── AuthenticationUpdateResponse.php │ │ ├── BigCommerceChannel.php │ │ ├── BigCommerceMetafield.php │ │ ├── BigQueryDataType.php │ │ ├── CommercetoolsCustomFields.php │ │ ├── DeleteResponse.php │ │ ├── Destination.php │ │ ├── DestinationCreate.php │ │ ├── DestinationCreateResponse.php │ │ ├── DestinationInput.php │ │ ├── DestinationSearch.php │ │ ├── DestinationSortKeys.php │ │ ├── DestinationType.php │ │ ├── DestinationUpdate.php │ │ ├── DestinationUpdateResponse.php │ │ ├── DockerStreams.php │ │ ├── DockerStreamsInput.php │ │ ├── DockerStreamsSyncMode.php │ │ ├── EmailNotifications.php │ │ ├── EntityType.php │ │ ├── ErrorBase.php │ │ ├── Event.php │ │ ├── EventSortKeys.php │ │ ├── EventStatus.php │ │ ├── EventType.php │ │ ├── ListAuthenticationsResponse.php │ │ ├── ListDestinationsResponse.php │ │ ├── ListEventsResponse.php │ │ ├── ListSourcesResponse.php │ │ ├── ListTasksResponse.php │ │ ├── ListTasksResponseV1.php │ │ ├── ListTransformationsResponse.php │ │ ├── MappingFieldDirective.php │ │ ├── MappingFormatSchema.php │ │ ├── MappingInput.php │ │ ├── MappingKitAction.php │ │ ├── MappingTypeCSV.php │ │ ├── MethodType.php │ │ ├── Notifications.php │ │ ├── OnDemandTrigger.php │ │ ├── OnDemandTriggerInput.php │ │ ├── OnDemandTriggerType.php │ │ ├── OrderKeys.php │ │ ├── Pagination.php │ │ ├── Platform.php │ │ ├── PlatformNone.php │ │ ├── PlatformWithNone.php │ │ ├── Policies.php │ │ ├── PushTaskPayload.php │ │ ├── PushTaskRecords.php │ │ ├── RecordType.php │ │ ├── Run.php │ │ ├── RunListResponse.php │ │ ├── RunOutcome.php │ │ ├── RunProgress.php │ │ ├── RunReasonCode.php │ │ ├── RunResponse.php │ │ ├── RunSortKeys.php │ │ ├── RunSourcePayload.php │ │ ├── RunSourceResponse.php │ │ ├── RunStatus.php │ │ ├── RunType.php │ │ ├── ScheduleTrigger.php │ │ ├── ScheduleTriggerInput.php │ │ ├── ScheduleTriggerType.php │ │ ├── ShopifyInput.php │ │ ├── ShopifyMarket.php │ │ ├── ShopifyMetafield.php │ │ ├── Source.php │ │ ├── SourceBigCommerce.php │ │ ├── SourceBigQuery.php │ │ ├── SourceCSV.php │ │ ├── SourceCommercetools.php │ │ ├── SourceCreate.php │ │ ├── SourceCreateResponse.php │ │ ├── SourceDocker.php │ │ ├── SourceGA4BigQueryExport.php │ │ ├── SourceInput.php │ │ ├── SourceJSON.php │ │ ├── SourceSearch.php │ │ ├── SourceShopify.php │ │ ├── SourceSortKeys.php │ │ ├── SourceType.php │ │ ├── SourceUpdate.php │ │ ├── SourceUpdateCommercetools.php │ │ ├── SourceUpdateDocker.php │ │ ├── SourceUpdateInput.php │ │ ├── SourceUpdateResponse.php │ │ ├── SourceUpdateShopify.php │ │ ├── StreamingInput.php │ │ ├── StreamingTrigger.php │ │ ├── StreamingTriggerType.php │ │ ├── SubscriptionTrigger.php │ │ ├── SubscriptionTriggerType.php │ │ ├── Task.php │ │ ├── TaskCreate.php │ │ ├── TaskCreateResponse.php │ │ ├── TaskCreateTrigger.php │ │ ├── TaskCreateV1.php │ │ ├── TaskInput.php │ │ ├── TaskSearch.php │ │ ├── TaskSortKeys.php │ │ ├── TaskUpdate.php │ │ ├── TaskUpdateResponse.php │ │ ├── TaskUpdateV1.php │ │ ├── TaskV1.php │ │ ├── Transformation.php │ │ ├── TransformationCode.php │ │ ├── TransformationCreate.php │ │ ├── TransformationCreateResponse.php │ │ ├── TransformationError.php │ │ ├── TransformationInput.php │ │ ├── TransformationNoCode.php │ │ ├── TransformationSearch.php │ │ ├── TransformationSortKeys.php │ │ ├── TransformationTry.php │ │ ├── TransformationTryResponse.php │ │ ├── TransformationType.php │ │ ├── TransformationUpdateResponse.php │ │ ├── Trigger.php │ │ ├── TriggerType.php │ │ ├── TriggerUpdateInput.php │ │ ├── WatchResponse.php │ │ └── Window.php │ ├── Insights │ │ ├── AddToCartEvent.php │ │ ├── AddedToCartObjectIDs.php │ │ ├── AddedToCartObjectIDsAfterSearch.php │ │ ├── ClickEvent.php │ │ ├── ClickedFilters.php │ │ ├── ClickedObjectIDs.php │ │ ├── ClickedObjectIDsAfterSearch.php │ │ ├── ConversionEvent.php │ │ ├── ConvertedFilters.php │ │ ├── ConvertedObjectIDs.php │ │ ├── ConvertedObjectIDsAfterSearch.php │ │ ├── Discount.php │ │ ├── ErrorBase.php │ │ ├── EventsItems.php │ │ ├── EventsResponse.php │ │ ├── InsightsEvents.php │ │ ├── ObjectData.php │ │ ├── ObjectDataAfterSearch.php │ │ ├── Price.php │ │ ├── PurchaseEvent.php │ │ ├── PurchasedObjectIDs.php │ │ ├── PurchasedObjectIDsAfterSearch.php │ │ ├── Value.php │ │ ├── ViewEvent.php │ │ ├── ViewedFilters.php │ │ └── ViewedObjectIDs.php │ ├── ModelInterface.php │ ├── Monitoring │ │ ├── ErrorBase.php │ │ ├── Incident.php │ │ ├── IncidentEntry.php │ │ ├── IncidentsResponse.php │ │ ├── IndexingMetric.php │ │ ├── IndexingTimeResponse.php │ │ ├── InfrastructureResponse.php │ │ ├── InventoryResponse.php │ │ ├── LatencyMetric.php │ │ ├── LatencyResponse.php │ │ ├── Metric.php │ │ ├── Metrics.php │ │ ├── Period.php │ │ ├── ProbesMetric.php │ │ ├── Region.php │ │ ├── Server.php │ │ ├── ServerStatus.php │ │ ├── Status.php │ │ ├── StatusResponse.php │ │ ├── TimeEntry.php │ │ └── Type.php │ ├── Personalization │ │ ├── DeleteUserProfileResponse.php │ │ ├── ErrorBase.php │ │ ├── EventType.php │ │ ├── EventsScoring.php │ │ ├── FacetsScoring.php │ │ ├── GetUserTokenResponse.php │ │ ├── PersonalizationStrategyParams.php │ │ └── SetPersonalizationStrategyResponse.php │ ├── QuerySuggestions │ │ ├── BaseResponse.php │ │ ├── ConfigStatus.php │ │ ├── Configuration.php │ │ ├── ConfigurationResponse.php │ │ ├── ConfigurationWithIndex.php │ │ ├── ErrorBase.php │ │ ├── Facet.php │ │ ├── Languages.php │ │ ├── LogFile.php │ │ ├── LogLevel.php │ │ └── SourceIndex.php │ ├── Recommend │ │ ├── AdvancedSyntaxFeatures.php │ │ ├── AlternativesAsExact.php │ │ ├── AroundPrecision.php │ │ ├── AroundRadius.php │ │ ├── AroundRadiusAll.php │ │ ├── AutoFacetFilter.php │ │ ├── Banner.php │ │ ├── BannerImage.php │ │ ├── BannerImageUrl.php │ │ ├── BannerLink.php │ │ ├── BooleanString.php │ │ ├── BoughtTogetherQuery.php │ │ ├── Condition.php │ │ ├── Consequence.php │ │ ├── DeletedAtResponse.php │ │ ├── Distinct.php │ │ ├── ErrorBase.php │ │ ├── ExactOnSingleWordQuery.php │ │ ├── Exhaustive.php │ │ ├── FacetFilters.php │ │ ├── FacetOrdering.php │ │ ├── FacetStats.php │ │ ├── FallbackParams.php │ │ ├── FbtModel.php │ │ ├── GetRecommendTaskResponse.php │ │ ├── GetRecommendationsParams.php │ │ ├── GetRecommendationsResponse.php │ │ ├── HideConsequenceObject.php │ │ ├── HighlightResult.php │ │ ├── HighlightResultOption.php │ │ ├── IgnorePlurals.php │ │ ├── IndexSettingsFacets.php │ │ ├── InsideBoundingBox.php │ │ ├── LookingSimilarModel.php │ │ ├── LookingSimilarQuery.php │ │ ├── MatchLevel.php │ │ ├── MatchedGeoLocation.php │ │ ├── NumericFilters.php │ │ ├── OptionalFilters.php │ │ ├── OptionalWords.php │ │ ├── ParamsConsequence.php │ │ ├── Personalization.php │ │ ├── PromoteConsequenceObject.php │ │ ├── QueryType.php │ │ ├── Range.php │ │ ├── RankingInfo.php │ │ ├── ReRankingApplyFilter.php │ │ ├── RecommendHit.php │ │ ├── RecommendModels.php │ │ ├── RecommendRule.php │ │ ├── RecommendSearchParams.php │ │ ├── RecommendUpdatedAtResponse.php │ │ ├── RecommendationsHit.php │ │ ├── RecommendationsRequest.php │ │ ├── RecommendationsResults.php │ │ ├── Redirect.php │ │ ├── RedirectRuleIndexData.php │ │ ├── RedirectRuleIndexMetadata.php │ │ ├── RedirectURL.php │ │ ├── RelatedModel.php │ │ ├── RelatedQuery.php │ │ ├── RemoveStopWords.php │ │ ├── RemoveWordsIfNoResults.php │ │ ├── RenderingContent.php │ │ ├── RuleMetadata.php │ │ ├── SearchRecommendRulesParams.php │ │ ├── SearchRecommendRulesResponse.php │ │ ├── SnippetResult.php │ │ ├── SnippetResultOption.php │ │ ├── SortRemainingBy.php │ │ ├── SupportedLanguage.php │ │ ├── TagFilters.php │ │ ├── TaskStatus.php │ │ ├── TimeRange.php │ │ ├── TrendingFacetHit.php │ │ ├── TrendingFacetsModel.php │ │ ├── TrendingFacetsQuery.php │ │ ├── TrendingItemsModel.php │ │ ├── TrendingItemsQuery.php │ │ ├── TypoTolerance.php │ │ ├── TypoToleranceEnum.php │ │ ├── Value.php │ │ └── Widgets.php │ └── Search │ │ ├── Acl.php │ │ ├── Action.php │ │ ├── AddApiKeyResponse.php │ │ ├── AdvancedSyntaxFeatures.php │ │ ├── AlternativesAsExact.php │ │ ├── Anchoring.php │ │ ├── ApiKey.php │ │ ├── ApiKeyOperation.php │ │ ├── AroundPrecision.php │ │ ├── AroundRadius.php │ │ ├── AroundRadiusAll.php │ │ ├── AssignUserIdParams.php │ │ ├── AttributeToUpdate.php │ │ ├── AutomaticFacetFilter.php │ │ ├── AutomaticFacetFilters.php │ │ ├── Banner.php │ │ ├── BannerImage.php │ │ ├── BannerImageUrl.php │ │ ├── BannerLink.php │ │ ├── BatchAssignUserIdsParams.php │ │ ├── BatchDictionaryEntriesParams.php │ │ ├── BatchDictionaryEntriesRequest.php │ │ ├── BatchParams.php │ │ ├── BatchRequest.php │ │ ├── BatchResponse.php │ │ ├── BatchWriteParams.php │ │ ├── BooleanString.php │ │ ├── BrowseParams.php │ │ ├── BrowseParamsObject.php │ │ ├── BrowseResponse.php │ │ ├── BuiltInOperation.php │ │ ├── BuiltInOperationType.php │ │ ├── BuiltInOperationValue.php │ │ ├── Condition.php │ │ ├── Consequence.php │ │ ├── ConsequenceHide.php │ │ ├── ConsequenceParams.php │ │ ├── ConsequenceQuery.php │ │ ├── ConsequenceQueryObject.php │ │ ├── CreatedAtResponse.php │ │ ├── DeleteApiKeyResponse.php │ │ ├── DeleteByParams.php │ │ ├── DeleteSourceResponse.php │ │ ├── DeletedAtResponse.php │ │ ├── DictionaryAction.php │ │ ├── DictionaryEntry.php │ │ ├── DictionaryEntryState.php │ │ ├── DictionaryEntryType.php │ │ ├── DictionaryLanguage.php │ │ ├── DictionarySettingsParams.php │ │ ├── DictionaryType.php │ │ ├── Distinct.php │ │ ├── Edit.php │ │ ├── EditType.php │ │ ├── ErrorBase.php │ │ ├── Event.php │ │ ├── EventStatus.php │ │ ├── EventType.php │ │ ├── ExactOnSingleWordQuery.php │ │ ├── Exhaustive.php │ │ ├── FacetFilters.php │ │ ├── FacetHits.php │ │ ├── FacetOrdering.php │ │ ├── FacetStats.php │ │ ├── Facets.php │ │ ├── FetchedIndex.php │ │ ├── GetApiKeyResponse.php │ │ ├── GetDictionarySettingsResponse.php │ │ ├── GetLogsResponse.php │ │ ├── GetObjectsParams.php │ │ ├── GetObjectsRequest.php │ │ ├── GetObjectsResponse.php │ │ ├── GetTaskResponse.php │ │ ├── GetTopUserIdsResponse.php │ │ ├── HasPendingMappingsResponse.php │ │ ├── HighlightResult.php │ │ ├── HighlightResultOption.php │ │ ├── Hit.php │ │ ├── IgnorePlurals.php │ │ ├── IndexSettings.php │ │ ├── InsideBoundingBox.php │ │ ├── Languages.php │ │ ├── ListApiKeysResponse.php │ │ ├── ListClustersResponse.php │ │ ├── ListIndicesResponse.php │ │ ├── ListUserIdsResponse.php │ │ ├── Log.php │ │ ├── LogQuery.php │ │ ├── LogType.php │ │ ├── MatchLevel.php │ │ ├── MatchedGeoLocation.php │ │ ├── Mode.php │ │ ├── MultipleBatchRequest.php │ │ ├── MultipleBatchResponse.php │ │ ├── NumericFilters.php │ │ ├── OperationIndexParams.php │ │ ├── OperationType.php │ │ ├── OptionalFilters.php │ │ ├── OptionalWords.php │ │ ├── Personalization.php │ │ ├── Promote.php │ │ ├── PromoteObjectID.php │ │ ├── PromoteObjectIDs.php │ │ ├── QueryType.php │ │ ├── Range.php │ │ ├── RankingInfo.php │ │ ├── ReRankingApplyFilter.php │ │ ├── Redirect.php │ │ ├── RedirectRuleIndexData.php │ │ ├── RedirectRuleIndexMetadata.php │ │ ├── RedirectURL.php │ │ ├── RemoveStopWords.php │ │ ├── RemoveUserIdResponse.php │ │ ├── RemoveWordsIfNoResults.php │ │ ├── RenderingContent.php │ │ ├── ReplaceAllObjectsResponse.php │ │ ├── ReplaceSourceResponse.php │ │ ├── Rule.php │ │ ├── SaveObjectResponse.php │ │ ├── SaveSynonymResponse.php │ │ ├── ScopeType.php │ │ ├── SearchDictionaryEntriesParams.php │ │ ├── SearchDictionaryEntriesResponse.php │ │ ├── SearchForFacetValuesRequest.php │ │ ├── SearchForFacetValuesResponse.php │ │ ├── SearchForFacets.php │ │ ├── SearchForHits.php │ │ ├── SearchMethodParams.php │ │ ├── SearchParams.php │ │ ├── SearchParamsObject.php │ │ ├── SearchParamsString.php │ │ ├── SearchQuery.php │ │ ├── SearchResponse.php │ │ ├── SearchResponses.php │ │ ├── SearchResult.php │ │ ├── SearchRulesParams.php │ │ ├── SearchRulesResponse.php │ │ ├── SearchStrategy.php │ │ ├── SearchSynonymsParams.php │ │ ├── SearchSynonymsResponse.php │ │ ├── SearchTypeDefault.php │ │ ├── SearchTypeFacet.php │ │ ├── SearchUserIdsParams.php │ │ ├── SearchUserIdsResponse.php │ │ ├── SecuredApiKeyRestrictions.php │ │ ├── SemanticSearch.php │ │ ├── SettingsResponse.php │ │ ├── SnippetResult.php │ │ ├── SnippetResultOption.php │ │ ├── SortRemainingBy.php │ │ ├── Source.php │ │ ├── StandardEntries.php │ │ ├── SupportedLanguage.php │ │ ├── SynonymHit.php │ │ ├── SynonymType.php │ │ ├── TagFilters.php │ │ ├── TaskStatus.php │ │ ├── TimeRange.php │ │ ├── TypoTolerance.php │ │ ├── TypoToleranceEnum.php │ │ ├── UpdateApiKeyResponse.php │ │ ├── UpdatedAtResponse.php │ │ ├── UpdatedAtWithObjectIdResponse.php │ │ ├── UserHighlightResult.php │ │ ├── UserHit.php │ │ ├── UserId.php │ │ ├── Value.php │ │ ├── WatchResponse.php │ │ └── Widgets.php ├── ObjectSerializer.php ├── RequestOptions │ ├── RequestOptions.php │ └── RequestOptionsFactory.php ├── RetryStrategy │ ├── ApiWrapper.php │ ├── ApiWrapperInterface.php │ ├── ClusterHosts.php │ ├── Host.php │ └── HostCollection.php └── Support │ ├── AlgoliaAgent.php │ └── Helpers.php ├── phpstan.neon └── phpunit.xml.dist /.github/ISSUE_TEMPLATE/Bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report. 3 | title: '[bug]: ' 4 | labels: ['bug', 'triage'] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | ## Please help us help you! 10 | 11 | > [!IMPORTANT] 12 | > This repository is fully generated and all pull request will be rejected. 13 | > If you wish to contribute, please refer to the [contribution guidelines](https://github.com/algolia/api-clients-automation/blob/main/CONTRIBUTING.md) on the `api-clients-automation` repository. 14 | 15 | Before filing your issue, ask yourself: 16 | - Is there an issue already opened for this bug? 17 | - Can I reproduce it? 18 | 19 | If you are not sure about the origin of the issue, or if it impacts your customer experience, please contact [our support team](https://alg.li/support). 20 | - type: textarea 21 | attributes: 22 | label: Description 23 | description: A clear and concise description of what the bug is. 24 | validations: 25 | required: true 26 | - type: dropdown 27 | id: client 28 | attributes: 29 | label: Client 30 | description: Which API are you targeting? 31 | options: 32 | - All 33 | - AB testing 34 | - Analytics 35 | - Ingestion 36 | - Insights 37 | - Monitoring 38 | - Personalization 39 | - Query-Suggestions 40 | - Recommend 41 | - Search 42 | - Crawler 43 | validations: 44 | required: true 45 | - type: input 46 | id: version 47 | attributes: 48 | label: Version 49 | description: The version of the client you are using. 50 | validations: 51 | required: true 52 | - type: textarea 53 | id: logs 54 | attributes: 55 | label: Relevant log output 56 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 57 | render: shell 58 | -------------------------------------------------------------------------------- /.github/workflows/do-not-edit-this-repository.yml: -------------------------------------------------------------------------------- 1 | name: Do not edit files in this repository 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - synchronize 8 | - reopen 9 | branches: 10 | - 'main' 11 | 12 | jobs: 13 | auto_close_pr: 14 | name: Close PR 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Close PR 20 | env: 21 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | PR_NUMBER: ${{ github.event.pull_request.number }} 23 | run: | 24 | gh pr close "${PR_NUMBER}" -d -c "Thanks for contributing to our API clients! Sorry to close your PR, but this repository is fully generated, you can port your changes to [the API Clients Automation repository](https://github.com/algolia/api-clients-automation). If you need some guidance, feel free to [open an issue](https://github.com/algolia/api-clients-automation/issues) or [read our contribution guide](https://api-clients-automation.netlify.app/docs/introduction)." 25 | -------------------------------------------------------------------------------- /.github/workflows/issue.yml: -------------------------------------------------------------------------------- 1 | name: 'Issue sync with Jira' 2 | on: 3 | issues: 4 | types: [opened] 5 | 6 | permissions: 7 | issues: write 8 | contents: read 9 | 10 | jobs: 11 | sync: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Create ticket 15 | uses: actions/github-script@v7 16 | with: 17 | script: | 18 | const action = context.payload.action; 19 | if (action !== 'opened') { 20 | return; 21 | } 22 | const title = context.payload.issue.title; 23 | const body = context.payload.issue.body; 24 | 25 | const res = await fetch('https://algolia.atlassian.net/rest/api/2/issue', { 26 | method: 'POST', 27 | headers: { 28 | 'Accept': 'application/json', 29 | 'Content-Type': 'application/json', 30 | 'Authorization': `Basic ${{ secrets.JIRA_TOKEN }}` 31 | }, 32 | body: JSON.stringify({ 33 | fields: { 34 | description: `Issue created by ${context.actor} at ${context.payload.issue.html_url} \n\n${body}`, 35 | issuetype: { 36 | id: '10001' 37 | }, 38 | parent: { 39 | key: 'DI-3523' 40 | }, 41 | project: { 42 | id: '10118' 43 | }, 44 | components: [ 45 | { 46 | id: '10872' 47 | } 48 | ], 49 | summary: `[GH-ISSUE] ${title}` 50 | }, 51 | update: {} 52 | }) 53 | }); 54 | 55 | if (!res.ok) { 56 | throw new Error(`Failed to create ticket: ${res.statusText} (${res.status}) - ${await res.text()}`); 57 | } 58 | 59 | const data = await res.json(); 60 | console.log(`Created ticket: ${data.key}`); 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ref: https://github.com/github/gitignore/blob/master/Composer.gitignore 2 | 3 | composer.phar 4 | /vendor/ 5 | 6 | # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control 7 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file 8 | # composer.lock 9 | 10 | # php-cs-fixer cache 11 | .php_cs.cache 12 | .php-cs-fixer.cache 13 | 14 | # PHPUnit cache 15 | .phpunit.result.cache 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013-Present Algolia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Algolia for PHP 4 | 5 | 6 |

The perfect starting point to integrate Algolia within your PHP project

7 | 8 |

9 | CircleCI 10 | Total Downloads 11 | Latest Version 12 | License 13 |

14 |

15 | 16 |

17 | Documentation • 18 | Laravel • 19 | Symfony • 20 | Community Forum • 21 | Stack Overflow • 22 | Report a bug • 23 | FAQ • 24 | Support 25 |

26 | 27 | ## ✨ Features 28 | 29 | - Thin & minimal low-level HTTP client to interact with Algolia's API 30 | - Supports php `^8.0`. 31 | 32 | ## 💡 Getting Started 33 | 34 | First, install Algolia PHP API Client via the [composer](https://getcomposer.org/) package manager: 35 | 36 | ```bash 37 | composer require algolia/algoliasearch-client-php "^4.0" 38 | ``` 39 | 40 | You can now import the Algolia API client in your project and play with it. 41 | 42 | ```php 43 | use Algolia\AlgoliaSearch\Api\SearchClient; 44 | 45 | $client = SearchClient::create('', ''); 46 | 47 | // Add a new record to your Algolia index 48 | $response = $client->saveObject( 49 | '', 50 | ['objectID' => 'id', 51 | 'test' => 'val', 52 | ], 53 | ); 54 | 55 | // play with the response 56 | var_dump($response); 57 | 58 | // Poll the task status to know when it has been indexed 59 | $client->waitForTask('', $response['taskID']); 60 | 61 | // Fetch search results, with typo tolerance 62 | $response = $client->search( 63 | ['requests' => [ 64 | ['indexName' => '', 65 | 'query' => '', 66 | 'hitsPerPage' => 50, 67 | ], 68 | ], 69 | ], 70 | ); 71 | 72 | // play with the response 73 | var_dump($response); 74 | ``` 75 | 76 | For full documentation, visit the **[Algolia PHP API Client](https://www.algolia.com/doc/libraries/php/)**. 77 | 78 | ## ❓ Troubleshooting 79 | 80 | Encountering an issue? Before reaching out to support, we recommend heading to our [FAQ](https://www.algolia.com/doc/api-client/troubleshooting/faq/php/) where you will find answers for the most common issues and gotchas with the client. You can also open [a GitHub issue](https://github.com/algolia/api-clients-automation/issues/new?assignees=&labels=&projects=&template=Bug_report.md) 81 | 82 | ## Contributing 83 | 84 | This repository hosts the code of the generated Algolia API client for PHP, if you'd like to contribute, head over to the [main repository](https://github.com/algolia/api-clients-automation). You can also find contributing guides on [our documentation website](https://api-clients-automation.netlify.app/docs/introduction). 85 | 86 | ## 📄 License 87 | 88 | The Algolia PHP API Client is an open-sourced software licensed under the [MIT license](LICENSE). 89 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "algolia/algoliasearch-client-php", 3 | "version": "4.21.0", 4 | "description": "API powering the features of Algolia.", 5 | "keywords": ["algolia", "search", "api", "client", "php"], 6 | "type": "library", 7 | "homepage": "https://github.com/algolia/algoliasearch-client-php", 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Algolia Team", 12 | "homepage": "https://alg.li/support" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=8.1 !=8.3.0", 17 | "ext-curl": "*", 18 | "ext-json": "*", 19 | "ext-mbstring": "*", 20 | "guzzlehttp/psr7": "^2.0", 21 | "psr/http-message": "^1.1 || ^2.0", 22 | "psr/log": "^1.0 || ^2.0 || ^3.0", 23 | "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" 24 | }, 25 | "require-dev": { 26 | "friendsofphp/php-cs-fixer": "^2.0 || ^3.5.0", 27 | "phpunit/phpunit": "^11.0", 28 | "vlucas/phpdotenv": "^5.4", 29 | "phpstan/phpstan": "^1.12" 30 | }, 31 | "autoload": { 32 | "psr-4": { "Algolia\\AlgoliaSearch\\" : "lib/" }, 33 | "files": [ 34 | "lib/Http/Psr7/functions.php" 35 | ] 36 | }, 37 | "suggest": { 38 | "guzzlehttp/guzzle": "If you prefer to use Guzzle HTTP client instead of the Http Client implementation provided by the package" 39 | }, 40 | "minimum-stability": "dev", 41 | "prefer-stable": true 42 | } 43 | -------------------------------------------------------------------------------- /lib/Algolia.php: -------------------------------------------------------------------------------- 1 | responseHeaders = $responseHeaders; 45 | $this->responseBody = $responseBody; 46 | } 47 | 48 | /** 49 | * Gets the HTTP response header. 50 | * 51 | * @return null|string[] HTTP response header 52 | */ 53 | public function getResponseHeaders() 54 | { 55 | return $this->responseHeaders; 56 | } 57 | 58 | /** 59 | * Gets the HTTP body of the server response either as Json or string. 60 | * 61 | * @return null|\stdClass|string HTTP body of the server response either as \stdClass or string 62 | */ 63 | public function getResponseBody() 64 | { 65 | return $this->responseBody; 66 | } 67 | 68 | /** 69 | * Sets the deserialized response object (during deserialization). 70 | * 71 | * @param mixed $obj Deserialized response object 72 | */ 73 | public function setResponseObject($obj) 74 | { 75 | $this->responseObject = $obj; 76 | } 77 | 78 | /** 79 | * Gets the deserialized response object (during deserialization). 80 | * 81 | * @return mixed the deserialized response object 82 | */ 83 | public function getResponseObject() 84 | { 85 | return $this->responseObject; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/Cache/NullCacheDriver.php: -------------------------------------------------------------------------------- 1 | '', 33 | 'apiKey' => '', 34 | 'hosts' => null, 35 | 'hasFullHosts' => false, 36 | 'readTimeout' => 5, 37 | 'writeTimeout' => 30, 38 | 'connectTimeout' => 2, 39 | 'defaultHeaders' => [], 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/Configuration/AnalyticsConfig.php: -------------------------------------------------------------------------------- 1 | '', 33 | 'apiKey' => '', 34 | 'hosts' => null, 35 | 'hasFullHosts' => false, 36 | 'readTimeout' => 5, 37 | 'writeTimeout' => 30, 38 | 'connectTimeout' => 2, 39 | 'defaultHeaders' => [], 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/Configuration/CompositionConfig.php: -------------------------------------------------------------------------------- 1 | '', 15 | 'apiKey' => '', 16 | 'hosts' => null, 17 | 'hasFullHosts' => false, 18 | 'readTimeout' => 5, 19 | 'writeTimeout' => 30, 20 | 'connectTimeout' => 2, 21 | 'defaultHeaders' => [], 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/Configuration/ConfigWithRegion.php: -------------------------------------------------------------------------------- 1 | $appId, 11 | 'apiKey' => $apiKey, 12 | 'region' => $region, 13 | ]; 14 | 15 | return new static($config); 16 | } 17 | 18 | public function getRegion() 19 | { 20 | return $this->config['region']; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/Configuration/IngestionConfig.php: -------------------------------------------------------------------------------- 1 | '', 34 | 'apiKey' => '', 35 | 'hosts' => null, 36 | 'hasFullHosts' => false, 37 | 'readTimeout' => 25, 38 | 'writeTimeout' => 25, 39 | 'connectTimeout' => 25, 40 | 'defaultHeaders' => [], 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Configuration/InsightsConfig.php: -------------------------------------------------------------------------------- 1 | '', 33 | 'apiKey' => '', 34 | 'hosts' => null, 35 | 'hasFullHosts' => false, 36 | 'readTimeout' => 5, 37 | 'writeTimeout' => 30, 38 | 'connectTimeout' => 2, 39 | 'defaultHeaders' => [], 40 | ]; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/Configuration/MonitoringConfig.php: -------------------------------------------------------------------------------- 1 | '', 15 | 'apiKey' => '', 16 | 'hosts' => null, 17 | 'hasFullHosts' => false, 18 | 'readTimeout' => 5, 19 | 'writeTimeout' => 30, 20 | 'connectTimeout' => 2, 21 | 'defaultHeaders' => [], 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/Configuration/PersonalizationConfig.php: -------------------------------------------------------------------------------- 1 | '', 34 | 'apiKey' => '', 35 | 'hosts' => null, 36 | 'hasFullHosts' => false, 37 | 'readTimeout' => 5, 38 | 'writeTimeout' => 30, 39 | 'connectTimeout' => 2, 40 | 'defaultHeaders' => [], 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Configuration/QuerySuggestionsConfig.php: -------------------------------------------------------------------------------- 1 | '', 34 | 'apiKey' => '', 35 | 'hosts' => null, 36 | 'hasFullHosts' => false, 37 | 'readTimeout' => 5, 38 | 'writeTimeout' => 30, 39 | 'connectTimeout' => 2, 40 | 'defaultHeaders' => [], 41 | ]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Configuration/RecommendConfig.php: -------------------------------------------------------------------------------- 1 | '', 15 | 'apiKey' => '', 16 | 'hosts' => null, 17 | 'hasFullHosts' => false, 18 | 'readTimeout' => 5, 19 | 'writeTimeout' => 30, 20 | 'connectTimeout' => 2, 21 | 'defaultHeaders' => [], 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/Configuration/SearchConfig.php: -------------------------------------------------------------------------------- 1 | $appId, 18 | 'apiKey' => $apiKey, 19 | ]; 20 | 21 | return new static($config); 22 | } 23 | 24 | public function getWaitTaskTimeBeforeRetry() 25 | { 26 | return $this->config['waitTaskTimeBeforeRetry']; 27 | } 28 | 29 | /** 30 | * Sets the max retries value used in the Search helpers (e.g. replaceAllobjects). 31 | * 32 | * @param number $maxRetries the user agent of the api client 33 | * 34 | * @return $this 35 | */ 36 | public function setMaxRetries($maxRetries) 37 | { 38 | $this->config['defaultMaxRetries'] = $maxRetries; 39 | 40 | return $this; 41 | } 42 | 43 | public function getDefaultMaxRetries() 44 | { 45 | return $this->config['defaultMaxRetries']; 46 | } 47 | 48 | /** 49 | * Sets the region of the current algolia application to the configuration, this is required to be called if you wish to leverage the transformation pipeline (via the *WithTransformation methods). 50 | * 51 | * @param string $region the user agent of the api client 52 | * 53 | * @return $this 54 | */ 55 | public function setTransformationRegion($region) 56 | { 57 | $this->config['region'] = $region; 58 | 59 | return $this; 60 | } 61 | 62 | public function getTransformationRegion() 63 | { 64 | return $this->config['region']; 65 | } 66 | 67 | public function getDefaultConfiguration() 68 | { 69 | return [ 70 | 'appId' => '', 71 | 'apiKey' => '', 72 | 'hosts' => null, 73 | 'hasFullHosts' => false, 74 | 'readTimeout' => 5, 75 | 'writeTimeout' => 30, 76 | 'connectTimeout' => 2, 77 | 'defaultHeaders' => [], 78 | 'region' => null, 79 | 'waitTaskTimeBeforeRetry' => $this->defaultWaitTaskTimeBeforeRetry, 80 | 'defaultMaxRetries' => $this->defaultMaxRetries, 81 | 'defaultForwardToReplicas' => null, 82 | 'batchSize' => 1000, 83 | ]; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/Exceptions/AlgoliaException.php: -------------------------------------------------------------------------------- 1 | request = $request; 14 | 15 | return $this; 16 | } 17 | 18 | public function getRequest() 19 | { 20 | return $this->request; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/Exceptions/RetriableException.php: -------------------------------------------------------------------------------- 1 | client = $client ?: self::buildClient(); 20 | } 21 | 22 | public function sendRequest( 23 | RequestInterface $request, 24 | $timeout, 25 | $connectTimeout 26 | ) { 27 | try { 28 | $response = $this->client->send($request, [ 29 | 'timeout' => $timeout, 30 | 'connect_timeout' => $connectTimeout, 31 | ]); 32 | } catch (RequestException $e) { 33 | if ($e->hasResponse()) { 34 | return $e->getResponse(); 35 | } 36 | 37 | return new Response(0, [], null, '1.1', $e->getMessage()); 38 | } catch (ConnectException $e) { 39 | return new Response(0, [], null, '1.1', $e->getMessage()); 40 | } 41 | 42 | return $response; 43 | } 44 | 45 | private static function buildClient(array $config = []) 46 | { 47 | $handlerStack = new HandlerStack(\GuzzleHttp\choose_handler()); 48 | $handlerStack->push(Middleware::prepareBody(), 'prepare_body'); 49 | $config = array_merge(['handler' => $handlerStack], $config); 50 | 51 | return new GuzzleClient($config); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/Http/HttpClientInterface.php: -------------------------------------------------------------------------------- 1 | hwm = $hwm; 33 | } 34 | 35 | public function __toString(): string 36 | { 37 | return $this->getContents(); 38 | } 39 | 40 | public function getContents(): string 41 | { 42 | $buffer = $this->buffer; 43 | $this->buffer = ''; 44 | 45 | return $buffer; 46 | } 47 | 48 | public function close(): void 49 | { 50 | $this->buffer = ''; 51 | } 52 | 53 | public function detach(): void 54 | { 55 | $this->close(); 56 | } 57 | 58 | public function getSize(): int 59 | { 60 | return mb_strlen($this->buffer); 61 | } 62 | 63 | public function isReadable(): bool 64 | { 65 | return true; 66 | } 67 | 68 | public function isWritable(): bool 69 | { 70 | return true; 71 | } 72 | 73 | public function isSeekable(): bool 74 | { 75 | return false; 76 | } 77 | 78 | public function rewind(): void 79 | { 80 | $this->seek(0); 81 | } 82 | 83 | public function seek($offset, $whence = SEEK_SET): void 84 | { 85 | throw new \RuntimeException('Cannot seek a BufferStream'); 86 | } 87 | 88 | public function eof(): bool 89 | { 90 | return 0 === mb_strlen($this->buffer); 91 | } 92 | 93 | public function tell(): int 94 | { 95 | throw new \RuntimeException('Cannot determine the position of a BufferStream'); 96 | } 97 | 98 | /** 99 | * Reads data from the buffer. 100 | * 101 | * @param mixed $length 102 | */ 103 | public function read($length): string 104 | { 105 | $currentLength = mb_strlen($this->buffer); 106 | 107 | if ($length >= $currentLength) { 108 | // No need to slice the buffer because we don't have enough data. 109 | $result = $this->buffer; 110 | $this->buffer = ''; 111 | } else { 112 | // Slice up the result to provide a subset of the buffer. 113 | $result = mb_substr($this->buffer, 0, $length); 114 | $this->buffer = mb_substr($this->buffer, $length); 115 | } 116 | 117 | return $result; 118 | } 119 | 120 | /** 121 | * Writes data to the buffer. 122 | * 123 | * @param mixed $string 124 | */ 125 | public function write($string): int 126 | { 127 | $this->buffer .= $string; 128 | 129 | // TODO: What should happen here? 130 | if (mb_strlen($this->buffer) >= $this->hwm) { 131 | return false; 132 | } 133 | 134 | return mb_strlen($string); 135 | } 136 | 137 | public function getMetadata($key = null) 138 | { 139 | if ('hwm' === $key) { 140 | return $this->hwm; 141 | } 142 | 143 | return $key ? null : []; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /lib/Http/Psr7/PumpStream.php: -------------------------------------------------------------------------------- 1 | source = $source; 49 | $this->size = isset($options['size']) ? $options['size'] : null; 50 | $this->metadata = isset($options['metadata']) 51 | ? $options['metadata'] 52 | : []; 53 | $this->buffer = new BufferStream(); 54 | } 55 | 56 | public function __toString(): string 57 | { 58 | try { 59 | return copy_to_string($this); 60 | } catch (\Exception $e) { 61 | return ''; 62 | } 63 | } 64 | 65 | public function close(): void 66 | { 67 | $this->detach(); 68 | } 69 | 70 | public function detach(): void 71 | { 72 | $this->tellPos = false; 73 | $this->source = null; 74 | } 75 | 76 | public function getSize(): ?int 77 | { 78 | return $this->size; 79 | } 80 | 81 | public function tell(): int 82 | { 83 | return $this->tellPos; 84 | } 85 | 86 | public function eof(): bool 87 | { 88 | return !$this->source; 89 | } 90 | 91 | public function isSeekable(): bool 92 | { 93 | return false; 94 | } 95 | 96 | public function rewind(): void 97 | { 98 | $this->seek(0); 99 | } 100 | 101 | public function seek($offset, $whence = SEEK_SET): void 102 | { 103 | throw new \RuntimeException('Cannot seek a PumpStream'); 104 | } 105 | 106 | public function isWritable(): bool 107 | { 108 | return false; 109 | } 110 | 111 | public function write($string): int 112 | { 113 | throw new \RuntimeException('Cannot write to a PumpStream'); 114 | } 115 | 116 | public function isReadable(): bool 117 | { 118 | return true; 119 | } 120 | 121 | public function read($length): string 122 | { 123 | $data = $this->buffer->read($length); 124 | $readLen = mb_strlen($data); 125 | $this->tellPos += $readLen; 126 | $remaining = $length - $readLen; 127 | 128 | if ($remaining) { 129 | $this->pump($remaining); 130 | $data .= $this->buffer->read($remaining); 131 | $this->tellPos += mb_strlen($data) - $readLen; 132 | } 133 | 134 | return $data; 135 | } 136 | 137 | public function getContents(): string 138 | { 139 | $result = ''; 140 | while (!$this->eof()) { 141 | $result .= $this->read(1000000); 142 | } 143 | 144 | return $result; 145 | } 146 | 147 | public function getMetadata($key = null) 148 | { 149 | if (!$key) { 150 | return $this->metadata; 151 | } 152 | 153 | return isset($this->metadata[$key]) ? $this->metadata[$key] : null; 154 | } 155 | 156 | private function pump($length) 157 | { 158 | if ($this->source) { 159 | do { 160 | $data = call_user_func($this->source, $length); 161 | if (false === $data || null === $data) { 162 | $this->source = null; 163 | 164 | return; 165 | } 166 | $this->buffer->write($data); 167 | $length -= mb_strlen($data); 168 | } while ($length > 0); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /lib/Http/Psr7/functions.php: -------------------------------------------------------------------------------- 1 | valid()) { 35 | return false; 36 | } 37 | $result = $resource->current(); 38 | $resource->next(); 39 | 40 | return $result; 41 | }, $options); 42 | } elseif (method_exists($resource, '__toString')) { 43 | return stream_for((string) $resource, $options); 44 | } 45 | 46 | break; 47 | 48 | case 'NULL': 49 | return new Stream(fopen('php://temp', 'r+'), $options); 50 | } 51 | if (is_callable($resource)) { 52 | return new PumpStream($resource, $options); 53 | } 54 | 55 | throw new \InvalidArgumentException('Invalid resource type: '.gettype($resource)); 56 | } 57 | 58 | /** 59 | * @internal 60 | * 61 | * @param mixed $maxLen 62 | */ 63 | function copy_to_string(StreamInterface $stream, $maxLen = -1) 64 | { 65 | $buffer = ''; 66 | if (-1 === $maxLen) { 67 | while (!$stream->eof()) { 68 | $buf = $stream->read(1048576); 69 | // Using a loose equality here to match on '' and false. 70 | if (null === $buf) { 71 | break; 72 | } 73 | $buffer .= $buf; 74 | } 75 | 76 | return $buffer; 77 | } 78 | $len = 0; 79 | while (!$stream->eof() && $len < $maxLen) { 80 | $buf = $stream->read($maxLen - $len); 81 | // Using a loose equality here to match on '' and false. 82 | if (null === $buf) { 83 | break; 84 | } 85 | $buffer .= $buf; 86 | $len = mb_strlen($buffer); 87 | } 88 | 89 | return $buffer; 90 | } 91 | -------------------------------------------------------------------------------- /lib/Iterators/AbstractAlgoliaIterator.php: -------------------------------------------------------------------------------- 1 | indexName = $indexName; 48 | $this->searchClient = $searchClient; 49 | $this->requestOptions = $requestOptions + [ 50 | 'hitsPerPage' => 1000, 51 | ]; 52 | 53 | $this->fetchNextPage(); 54 | } 55 | 56 | /** 57 | * Return the current element. 58 | * 59 | * @return array 60 | */ 61 | #[\ReturnTypeWillChange] 62 | public function current() 63 | { 64 | $hit = $this->response['hits'][$this->batchKey]; 65 | 66 | return $this->formatHit($hit); 67 | } 68 | 69 | /** 70 | * Move forward to next element. 71 | */ 72 | #[\ReturnTypeWillChange] 73 | public function next() 74 | { 75 | ++$this->key; 76 | ++$this->batchKey; 77 | if ($this->valid()) { 78 | return; 79 | } 80 | 81 | $this->fetchNextPage(); 82 | } 83 | 84 | /** 85 | * Return the key of the current element. 86 | * 87 | * @return int 88 | */ 89 | #[\ReturnTypeWillChange] 90 | public function key() 91 | { 92 | return $this->key; 93 | } 94 | 95 | /** 96 | * Checks if current position is valid. If the current position 97 | * is not valid, we call Algolia' API to load more results 98 | * until it's the last page. 99 | * 100 | * @return bool the return value will be casted to boolean and then evaluated. 101 | * Returns true on success or false on failure 102 | */ 103 | #[\ReturnTypeWillChange] 104 | public function valid() 105 | { 106 | return isset($this->response['hits'][$this->batchKey]); 107 | } 108 | 109 | /** 110 | * Rewind the Iterator to the first element. 111 | */ 112 | #[\ReturnTypeWillChange] 113 | public function rewind() 114 | { 115 | if (0 !== $this->key) { 116 | $this->key = 0; 117 | $this->batchKey = 0; 118 | $this->page = 0; 119 | $this->response = null; 120 | $this->fetchNextPage(); 121 | } 122 | } 123 | 124 | /** 125 | * Call Algolia' API to get new result batch. 126 | */ 127 | abstract protected function fetchNextPage(); 128 | 129 | /** 130 | * Sometimes the Iterator is using search internally, this method 131 | * is used to clean the results, like remove the highlight. 132 | * 133 | * @return array formatted synonym array 134 | */ 135 | abstract protected function formatHit(array $hit); 136 | } 137 | -------------------------------------------------------------------------------- /lib/Iterators/ObjectIterator.php: -------------------------------------------------------------------------------- 1 | response['cursor']) 10 | ? $this->response['cursor'] 11 | : null; 12 | } 13 | 14 | /** 15 | * Exporting objects (records) doesn't use the search function but the 16 | * browse method, no client-side formatting is required. 17 | * 18 | * @return array the exact same $hit 19 | */ 20 | protected function formatHit(array $hit) 21 | { 22 | return $hit; 23 | } 24 | 25 | protected function fetchNextPage() 26 | { 27 | if (is_array($this->response) && !isset($this->response['cursor'])) { 28 | return; 29 | } 30 | 31 | $cursor = []; 32 | if (isset($this->response['cursor'])) { 33 | $cursor['cursor'] = $this->response['cursor']; 34 | } 35 | 36 | $this->response = $this->searchClient->browse( 37 | $this->indexName, 38 | array_merge($this->requestOptions, $cursor) 39 | ); 40 | 41 | $this->batchKey = 0; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Iterators/RuleIterator.php: -------------------------------------------------------------------------------- 1 | response) 18 | && $this->key >= count($this->response['hits']) 19 | ) { 20 | return; 21 | } 22 | 23 | $this->response = $this->searchClient->searchRules( 24 | $this->indexName, 25 | array_merge($this->requestOptions, ['page' => $this->page]) 26 | ); 27 | 28 | $this->batchKey = 0; 29 | ++$this->page; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/Iterators/SynonymIterator.php: -------------------------------------------------------------------------------- 1 | response) 18 | && $this->key >= count($this->response['hits']) 19 | ) { 20 | return; 21 | } 22 | 23 | $this->response = $this->searchClient->searchSynonyms( 24 | $this->indexName, 25 | array_merge($this->requestOptions, ['page' => $this->page]) 26 | ); 27 | 28 | $this->batchKey = 0; 29 | ++$this->page; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/Log/DebugLogger.php: -------------------------------------------------------------------------------- 1 | $level, 43 | 'message' => $message, 44 | 'context' => $context, 45 | ]; 46 | 47 | if (function_exists('dump')) { 48 | dump($logMessage); 49 | } else { 50 | var_dump($logMessage); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/Model/AbstractModel.php: -------------------------------------------------------------------------------- 1 | 50`, `>` is the operator. 13 | */ 14 | class Operator 15 | { 16 | /** 17 | * Possible values of this enum. 18 | */ 19 | public const COLON = ':'; 20 | 21 | public const LESS_THAN = '<'; 22 | 23 | public const LESS_THAN_OR_EQUAL_TO = '<='; 24 | 25 | public const EQUAL = '='; 26 | 27 | public const NOT_EQUAL = '!='; 28 | 29 | public const GREATER_THAN = '>'; 30 | 31 | public const GREATER_THAN_OR_EQUAL_TO = '>='; 32 | 33 | /** 34 | * Gets allowable values of the enum. 35 | * 36 | * @return string[] 37 | */ 38 | public static function getAllowableEnumValues() 39 | { 40 | return [ 41 | self::COLON, 42 | self::LESS_THAN, 43 | self::LESS_THAN_OR_EQUAL_TO, 44 | self::EQUAL, 45 | self::NOT_EQUAL, 46 | self::GREATER_THAN, 47 | self::GREATER_THAN_OR_EQUAL_TO, 48 | ]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/Model/Analytics/OrderBy.php: -------------------------------------------------------------------------------- 1 | listInvalidProperties()); 140 | } 141 | 142 | /** 143 | * Returns true if offset exists. False otherwise. 144 | * 145 | * @param int $offset Offset 146 | */ 147 | public function offsetExists($offset): bool 148 | { 149 | return isset($this->container[$offset]); 150 | } 151 | 152 | /** 153 | * Gets offset. 154 | * 155 | * @param int $offset Offset 156 | * 157 | * @return null|mixed 158 | */ 159 | public function offsetGet($offset): mixed 160 | { 161 | return $this->container[$offset] ?? null; 162 | } 163 | 164 | /** 165 | * Sets value based on offset. 166 | * 167 | * @param null|int $offset Offset 168 | * @param mixed $value Value to be set 169 | */ 170 | public function offsetSet($offset, $value): void 171 | { 172 | if (is_null($offset)) { 173 | $this->container[] = $value; 174 | } else { 175 | $this->container[$offset] = $value; 176 | } 177 | } 178 | 179 | /** 180 | * Unsets offset. 181 | * 182 | * @param int $offset Offset 183 | */ 184 | public function offsetUnset($offset): void 185 | { 186 | unset($this->container[$offset]); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/Model/Composition/MatchLevel.php: -------------------------------------------------------------------------------- 1 | listInvalidProperties()); 140 | } 141 | 142 | /** 143 | * Returns true if offset exists. False otherwise. 144 | * 145 | * @param int $offset Offset 146 | */ 147 | public function offsetExists($offset): bool 148 | { 149 | return isset($this->container[$offset]); 150 | } 151 | 152 | /** 153 | * Gets offset. 154 | * 155 | * @param int $offset Offset 156 | * 157 | * @return null|mixed 158 | */ 159 | public function offsetGet($offset): mixed 160 | { 161 | return $this->container[$offset] ?? null; 162 | } 163 | 164 | /** 165 | * Sets value based on offset. 166 | * 167 | * @param null|int $offset Offset 168 | * @param mixed $value Value to be set 169 | */ 170 | public function offsetSet($offset, $value): void 171 | { 172 | if (is_null($offset)) { 173 | $this->container[] = $value; 174 | } else { 175 | $this->container[$offset] = $value; 176 | } 177 | } 178 | 179 | /** 180 | * Unsets offset. 181 | * 182 | * @param int $offset Offset 183 | */ 184 | public function offsetUnset($offset): void 185 | { 186 | unset($this->container[$offset]); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/Model/Ingestion/RecordType.php: -------------------------------------------------------------------------------- 1 | listInvalidProperties()); 140 | } 141 | 142 | /** 143 | * Returns true if offset exists. False otherwise. 144 | * 145 | * @param int $offset Offset 146 | */ 147 | public function offsetExists($offset): bool 148 | { 149 | return isset($this->container[$offset]); 150 | } 151 | 152 | /** 153 | * Gets offset. 154 | * 155 | * @param int $offset Offset 156 | * 157 | * @return null|mixed 158 | */ 159 | public function offsetGet($offset): mixed 160 | { 161 | return $this->container[$offset] ?? null; 162 | } 163 | 164 | /** 165 | * Sets value based on offset. 166 | * 167 | * @param null|int $offset Offset 168 | * @param mixed $value Value to be set 169 | */ 170 | public function offsetSet($offset, $value): void 171 | { 172 | if (is_null($offset)) { 173 | $this->container[] = $value; 174 | } else { 175 | $this->container[$offset] = $value; 176 | } 177 | } 178 | 179 | /** 180 | * Unsets offset. 181 | * 182 | * @param int $offset Offset 183 | */ 184 | public function offsetUnset($offset): void 185 | { 186 | unset($this->container[$offset]); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/Model/Recommend/LookingSimilarModel.php: -------------------------------------------------------------------------------- 1 | listInvalidProperties()); 140 | } 141 | 142 | /** 143 | * Returns true if offset exists. False otherwise. 144 | * 145 | * @param int $offset Offset 146 | */ 147 | public function offsetExists($offset): bool 148 | { 149 | return isset($this->container[$offset]); 150 | } 151 | 152 | /** 153 | * Gets offset. 154 | * 155 | * @param int $offset Offset 156 | * 157 | * @return null|mixed 158 | */ 159 | public function offsetGet($offset): mixed 160 | { 161 | return $this->container[$offset] ?? null; 162 | } 163 | 164 | /** 165 | * Sets value based on offset. 166 | * 167 | * @param null|int $offset Offset 168 | * @param mixed $value Value to be set 169 | */ 170 | public function offsetSet($offset, $value): void 171 | { 172 | if (is_null($offset)) { 173 | $this->container[] = $value; 174 | } else { 175 | $this->container[$offset] = $value; 176 | } 177 | } 178 | 179 | /** 180 | * Unsets offset. 181 | * 182 | * @param int $offset Offset 183 | */ 184 | public function offsetUnset($offset): void 185 | { 186 | unset($this->container[$offset]); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/Model/Search/DictionaryAction.php: -------------------------------------------------------------------------------- 1 | listInvalidProperties()); 140 | } 141 | 142 | /** 143 | * Returns true if offset exists. False otherwise. 144 | * 145 | * @param int $offset Offset 146 | */ 147 | public function offsetExists($offset): bool 148 | { 149 | return isset($this->container[$offset]); 150 | } 151 | 152 | /** 153 | * Gets offset. 154 | * 155 | * @param int $offset Offset 156 | * 157 | * @return null|mixed 158 | */ 159 | public function offsetGet($offset): mixed 160 | { 161 | return $this->container[$offset] ?? null; 162 | } 163 | 164 | /** 165 | * Sets value based on offset. 166 | * 167 | * @param null|int $offset Offset 168 | * @param mixed $value Value to be set 169 | */ 170 | public function offsetSet($offset, $value): void 171 | { 172 | if (is_null($offset)) { 173 | $this->container[] = $value; 174 | } else { 175 | $this->container[$offset] = $value; 176 | } 177 | } 178 | 179 | /** 180 | * Unsets offset. 181 | * 182 | * @param int $offset Offset 183 | */ 184 | public function offsetUnset($offset): void 185 | { 186 | unset($this->container[$offset]); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /lib/Model/Search/LogType.php: -------------------------------------------------------------------------------- 1 | config = $config; 15 | } 16 | 17 | /** 18 | * @param array|RequestOptions $options 19 | * 20 | * @return RequestOptions 21 | */ 22 | public function create($options) 23 | { 24 | if (is_array($options)) { 25 | $options = $this->normalize($options); 26 | 27 | $options = new RequestOptions($options); 28 | } elseif ($options instanceof RequestOptions) { 29 | $options = $this->create($options); 30 | } else { 31 | throw new \InvalidArgumentException('RequestOptions can only be created from array or from RequestOptions object'); 32 | } 33 | 34 | return $options->addDefaultHeaders($this->config->getDefaultHeaders()); 35 | } 36 | 37 | public function createBodyLess($options) 38 | { 39 | $options = $this->create($options); 40 | 41 | return $options->addQueryParameters($options->getBody())->setBody([]); 42 | } 43 | 44 | private function normalize($options) 45 | { 46 | $normalized = [ 47 | 'headers' => [ 48 | 'x-algolia-application-id' => $this->config->getAppId(), 49 | 'x-algolia-api-key' => $this->config->getAlgoliaApiKey(), 50 | 'User-Agent' => null !== $this->config->getAlgoliaAgent() 51 | ? $this->config->getAlgoliaAgent() 52 | : AlgoliaAgent::get($this->config->getClientName()), 53 | 'Content-Type' => 'application/json', 54 | ], 55 | 'queryParameters' => [], 56 | 'body' => [], 57 | 'readTimeout' => $this->config->getReadTimeout(), 58 | 'writeTimeout' => $this->config->getWriteTimeout(), 59 | 'connectTimeout' => $this->config->getConnectTimeout(), 60 | ]; 61 | foreach ($options as $optionName => $value) { 62 | if (is_array($value) && 'headers' === $optionName) { 63 | $headersToLowerCase = []; 64 | foreach ($value as $key => $v) { 65 | $headersToLowerCase[mb_strtolower($key)] = $v; 66 | } 67 | 68 | $normalized[$optionName] = array_merge( 69 | $normalized[$optionName], 70 | $headersToLowerCase 71 | ); 72 | } else { 73 | $normalized[$optionName] = $value; 74 | } 75 | } 76 | 77 | return $normalized; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/RetryStrategy/ApiWrapperInterface.php: -------------------------------------------------------------------------------- 1 | read = $read; 25 | $this->write = $write; 26 | } 27 | 28 | public static function create($read, $write = null) 29 | { 30 | if (null === $write) { 31 | $write = $read; 32 | } 33 | 34 | if (is_string($read)) { 35 | $read = [$read => 0]; 36 | } 37 | 38 | if (is_string($write)) { 39 | $write = [$write => 0]; 40 | } 41 | 42 | if (array_values($read) === $read) { 43 | $read = array_fill_keys($read, 0); 44 | } 45 | 46 | if (array_values($write) === $write) { 47 | $write = array_fill_keys($write, 0); 48 | } 49 | 50 | return new self( 51 | HostCollection::create($read), 52 | HostCollection::create($write) 53 | ); 54 | } 55 | 56 | public static function createFromAppId($applicationId) 57 | { 58 | $read = $write = [ 59 | $applicationId.'-1.algolianet.com' => 0, 60 | $applicationId.'-2.algolianet.com' => 0, 61 | $applicationId.'-3.algolianet.com' => 0, 62 | ]; 63 | 64 | $read[$applicationId.'-dsn.algolia.net'] = 10; 65 | $write[$applicationId.'.algolia.net'] = 10; 66 | 67 | return self::create($read, $write)->shuffle(); 68 | } 69 | 70 | public static function createFromCache($cacheKey) 71 | { 72 | if (!Algolia::isCacheEnabled()) { 73 | return false; 74 | } 75 | 76 | if (!Algolia::getCache()->has($cacheKey)) { 77 | return false; 78 | } 79 | 80 | return @unserialize(Algolia::getCache()->get($cacheKey)); 81 | } 82 | 83 | public function read() 84 | { 85 | return $this->getUrls('read'); 86 | } 87 | 88 | public function write() 89 | { 90 | return $this->getUrls('write'); 91 | } 92 | 93 | public function failed($host) 94 | { 95 | $this->read->markAsDown($host); 96 | $this->write->markAsDown($host); 97 | 98 | $this->updateCache(); 99 | 100 | return $this; 101 | } 102 | 103 | public function reset() 104 | { 105 | $this->read->reset(); 106 | $this->write->reset(); 107 | 108 | return $this; 109 | } 110 | 111 | public function shuffle() 112 | { 113 | $this->read->shuffle(); 114 | $this->write->shuffle(); 115 | 116 | return $this; 117 | } 118 | 119 | /** 120 | * Sets the cache key to save the state of the ClusterHosts. 121 | * 122 | * @param string $cacheKey 123 | * 124 | * @return $this 125 | */ 126 | public function setCacheKey($cacheKey) 127 | { 128 | $this->cacheKey = $cacheKey; 129 | 130 | return $this; 131 | } 132 | 133 | private function getUrls($type) 134 | { 135 | $urls = $this->{$type}->getUrls(); 136 | $lashHashName = 'last'.ucfirst($type).'Hash'; 137 | 138 | if (Algolia::isCacheEnabled()) { 139 | $hash = sha1(implode('-', $urls)); 140 | if ($hash !== $this->{$lashHashName}) { 141 | $this->updateCache(); 142 | } 143 | $this->{$lashHashName} = $hash; 144 | } 145 | 146 | return $urls; 147 | } 148 | 149 | private function updateCache() 150 | { 151 | if (null !== $this->cacheKey && Algolia::isCacheEnabled()) { 152 | Algolia::getCache()->set($this->cacheKey, serialize($this)); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /lib/RetryStrategy/Host.php: -------------------------------------------------------------------------------- 1 | url = $url; 22 | $this->priority = $priority; 23 | } 24 | 25 | public function getUrl() 26 | { 27 | return $this->url; 28 | } 29 | 30 | public function getPriority() 31 | { 32 | return $this->priority; 33 | } 34 | 35 | public function isUp() 36 | { 37 | if (!$this->up) { 38 | $this->resetIfExpired(); 39 | } 40 | 41 | return $this->up; 42 | } 43 | 44 | public function markAsDown() 45 | { 46 | $this->up = false; 47 | $this->lastCheck = time(); 48 | } 49 | 50 | public function reset() 51 | { 52 | $this->up = true; 53 | $this->lastCheck = null; 54 | } 55 | 56 | private function resetIfExpired() 57 | { 58 | $expired = $this->lastCheck + self::TTL < time(); 59 | 60 | if ($expired) { 61 | $this->reset(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/RetryStrategy/HostCollection.php: -------------------------------------------------------------------------------- 1 | hosts = $hosts; 15 | } 16 | 17 | public static function create(array $urlsWithPriority) 18 | { 19 | $hosts = []; 20 | foreach ($urlsWithPriority as $url => $priority) { 21 | $hosts[] = new Host($url, $priority); 22 | } 23 | 24 | return new self($hosts); 25 | } 26 | 27 | public function get() 28 | { 29 | // We pass the result through array_values because sometimes 30 | // we need to make sure you can access the first element 31 | // via $result[0] 32 | return array_values( 33 | array_filter($this->hosts, function (Host $host) { 34 | return $host->isUp(); 35 | }) 36 | ); 37 | } 38 | 39 | public function getUrls() 40 | { 41 | return array_map(function (Host $host) { 42 | return $host->getUrl(); 43 | }, $this->get()); 44 | } 45 | 46 | public function markAsDown($hostKey) 47 | { 48 | array_map(function (Host $host) use ($hostKey) { 49 | if ($host->getUrl() === $hostKey) { 50 | $host->markAsDown(); 51 | } 52 | }, $this->hosts); 53 | } 54 | 55 | public function shuffle() 56 | { 57 | if (shuffle($this->hosts)) { 58 | $this->sort(); 59 | } 60 | 61 | return $this; 62 | } 63 | 64 | public function reset() 65 | { 66 | foreach ($this->hosts as $host) { 67 | $host->reset(); 68 | } 69 | 70 | return $this; 71 | } 72 | 73 | private function sort() 74 | { 75 | usort($this->hosts, function (Host $a, Host $b) { 76 | $prioA = $a->getPriority(); 77 | $prioB = $b->getPriority(); 78 | if ($prioA === $prioB) { 79 | return 0; 80 | } 81 | 82 | return $prioA > $prioB ? -1 : 1; 83 | }); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/Support/AlgoliaAgent.php: -------------------------------------------------------------------------------- 1 | $version) { 38 | $ua[] = $segment.' ('.$version.')'; 39 | } 40 | 41 | return implode('; ', $ua); 42 | } 43 | 44 | private static function getDefaultSegments($clientName) 45 | { 46 | $segments = []; 47 | 48 | $segments['Algolia for PHP'] = Algolia::VERSION; 49 | $segments[$clientName] = Algolia::VERSION; 50 | $segments['PHP'] = rtrim( 51 | str_replace(PHP_EXTRA_VERSION, '', PHP_VERSION), 52 | '-' 53 | ); 54 | if (defined('HHVM_VERSION')) { 55 | $segments['HHVM'] = HHVM_VERSION; 56 | } 57 | if (interface_exists('\GuzzleHttp\ClientInterface')) { 58 | if (defined('\GuzzleHttp\ClientInterface::VERSION')) { 59 | $segments['Guzzle'] = ClientInterface::VERSION; 60 | } else { 61 | $segments['Guzzle'] = 62 | ClientInterface::MAJOR_VERSION; 63 | } 64 | } 65 | 66 | return $segments; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | excludePaths: 3 | - vendor/* 4 | level: max 5 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ../../tests/output/php/src/ 6 | 7 | 8 | 9 | --------------------------------------------------------------------------------