From 5cad729f8231c02bdf1fe0d0ed7a6bcac8235051 Mon Sep 17 00:00:00 2001 From: Lachlan Reynolds Date: Fri, 15 May 2026 10:20:19 +1200 Subject: [PATCH 1/5] Added basic parameter examples, not currently showing complex types, fixed logic around basic array shapes --- Annotations/AnnotationGenerator.php | 179 +++++- config/ParameterExamples.php | 599 ++++++++++++++++++++ tests/Resources/MockAnnotationGenerator.php | 21 + tests/Unit/AnnotationGeneratorTest.php | 93 ++- 4 files changed, 882 insertions(+), 10 deletions(-) create mode 100644 config/ParameterExamples.php diff --git a/Annotations/AnnotationGenerator.php b/Annotations/AnnotationGenerator.php index ee04cc3..2dbc9d8 100644 --- a/Annotations/AnnotationGenerator.php +++ b/Annotations/AnnotationGenerator.php @@ -99,6 +99,11 @@ class AnnotationGenerator */ protected $allowLocalRequests; + /** + * @var array|null + */ + protected $parameterExamples; + public function __construct( DocumentationGenerator $generator, ?PathResolver $pathResolver = null, @@ -110,6 +115,7 @@ public function __construct( $this->artifactWriter = $artifactWriter ?? new ArtifactWriter(); $this->missingImportantDataWarnings = []; $this->allowLocalRequests = $allowLocalRequests; + $this->parameterExamples = null; $this->currentPluginDir = Manager::getInstance()::getPluginDirectory('OpenApiDocs'); } @@ -455,7 +461,7 @@ public function buildParameterAnnotationData(string $methodName, string $paramNa // Sometimes, doc-block can wrap type hinting with parenthesis. Remove them. $type = trim($type, '()'); // If the signature type is array, but the type hinting provides more, use that instead - if ($type === 'array' && strpos($docType, '[]') !== false && strpos($docType, '|') === false) { + if ($type === 'array' && $this->hasSpecificArrayShape($docType) && strpos($docType, '|') === false) { $type = $docType; } $typesMap = []; @@ -468,6 +474,7 @@ public function buildParameterAnnotationData(string $methodName, string $paramNa $typeHints = array_diff($typeHints, ['bool']); } + $isRequired = !key_exists('default', $paramMetadata) || $paramMetadata['default'] instanceof NoDefaultValue; $allTypeHintsAreStringLiterals = $this->areAllTypeHintsStringLiterals($typeHints); $enumValues = []; if ($allTypeHintsAreStringLiterals) { @@ -478,17 +485,13 @@ public function buildParameterAnnotationData(string $methodName, string $paramNa } else { foreach ($typeHints as $typePart) { $typePart = trim($typePart, ' ()'); - $normalisedType = $this->getOpenApiTypeFromPhpType($typePart); + $normalisedType = $this->hasSpecificArrayShape($typePart) ? 'array' : $this->getOpenApiTypeFromPhpType($typePart); // If the type is array, check if there's a subType - $subType = null; - if ($normalisedType === 'array' && $typePart !== 'array' && strpos($typePart, '[]') !== false) { - $subType = substr($typePart, 0, strpos($typePart, '[]')); - } + $subType = $this->getArraySubTypeFromPhpType($typePart, $normalisedType); $typesMap[$normalisedType] = $subType !== null ? $this->getOpenApiTypeFromPhpType($subType) : $subType; } } - $isRequired = !key_exists('default', $paramMetadata) || $paramMetadata['default'] instanceof NoDefaultValue; $description = $paramDocInfo['description'] ?? ''; if (empty($description)) { $this->addMissingImportantDataWarning($methodName, $paramName, 'Description is not specified in comment block.'); @@ -506,6 +509,13 @@ public function buildParameterAnnotationData(string $methodName, string $paramNa $example = trim($example, '"'); } + if ($isRequired && $example === '') { + $configExample = $this->getParameterExampleFromConfig($paramName, $type, $typesMap); + if ($configExample !== null) { + $example = $configExample; + } + } + // Clean up the descriptions a little more like removing linebreaks and escaping double-quotes $description = $this->normaliseDescriptionText($description); @@ -555,6 +565,138 @@ protected function areAllTypeHintsStringLiterals(array $typeHints): bool return true; } + protected function hasSpecificArrayShape(string $type): bool + { + return strpos($type, '[]') !== false || preg_match('/^(array|list)<.+>$/', trim($type)) === 1; + } + + protected function getArraySubTypeFromPhpType(string $typePart, string $normalisedType): ?string + { + if ($normalisedType !== 'array' || $typePart === 'array') { + return null; + } + + if (strpos($typePart, '[]') !== false) { + return substr($typePart, 0, strpos($typePart, '[]')); + } + + if (preg_match('/^(array|list)<(.+)>$/', trim($typePart), $matches) !== 1) { + return null; + } + + $genericParts = array_map('trim', explode(',', $matches[2], 2)); + if (count($genericParts) === 1) { + return $genericParts[0]; + } + + return $genericParts[1]; + } + + /** + * Load and return the configured parameter examples. + * + * @return array + */ + protected function getParameterExamplesConfig(): array + { + if ($this->parameterExamples !== null) { + return $this->parameterExamples; + } + + $configPath = $this->currentPluginDir . '/config/ParameterExamples.php'; + if (!is_file($configPath)) { + $this->parameterExamples = []; + return $this->parameterExamples; + } + + $config = require $configPath; + $this->parameterExamples = is_array($config) ? $config : []; + + return $this->parameterExamples; + } + + /** + * Return a config-backed example string if the configured example is intentionally simple enough to support. + */ + protected function getParameterExampleFromConfig(string $paramName, string $type, array $typesMap): ?string + { + $config = $this->getParameterExamplesConfig(); + $keysToTry = [ + $paramName . ':' . $type, + $paramName . ':' . preg_replace('/\s+/', '', $type), + ]; + + $configValue = null; + $foundConfigValue = false; + foreach (array_unique($keysToTry) as $key) { + if (!array_key_exists($key, $config)) { + continue; + } + + $configValue = $config[$key]; + $foundConfigValue = true; + break; + } + + if (!$foundConfigValue) { + return null; + } + + return $this->normaliseConfiguredParameterExample($configValue, $typesMap); + } + + /** + * Convert supported scalar/basic-array config values into the string form used by schema generation. + */ + protected function normaliseConfiguredParameterExample($example, array $typesMap = []): ?string + { + if (is_bool($example)) { + return $example ? 'true' : 'false'; + } + + if (is_int($example) || is_float($example) || is_string($example)) { + return strval($example); + } + + if ( + !is_array($example) + || !$this->isBasicExampleArray($example) + || !$this->supportsBasicArrayExample($typesMap) + ) { + return null; + } + + $encoded = json_encode(array_values($example)); + + return is_string($encoded) ? $encoded : null; + } + + /** + * Only use array config examples when the emitted schema includes an array shape. + */ + protected function supportsBasicArrayExample(array $typesMap): bool + { + return array_key_exists('array', $typesMap); + } + + /** + * Only support flat indexed arrays of scalar values for now. + */ + protected function isBasicExampleArray(array $example): bool + { + if (array_values($example) !== $example) { + return false; + } + + foreach ($example as $item) { + if (!is_bool($item) && !is_int($item) && !is_float($item) && !is_string($item)) { + return false; + } + } + + return true; + } + /** * Take description text and normalise it. This includes trimming surrounding whitespace, removing newlines and * escaping double-quote characters. @@ -1863,7 +2005,7 @@ public function buildSchemaObjectArray(string $type, string $subType = '', strin */ public function wrapStringWithQuotes(string $string, string $type, string $quoteCharacter = '"'): string { - if (in_array($type, ['integer', 'boolean', 'array'])) { + if (in_array($type, ['integer', 'number', 'boolean', 'array'])) { return $string; } @@ -1972,12 +2114,17 @@ public function compileOperationLines(string $path, string $opId, string $plugin $paramMap[] = 'description="' . $param['description'] . '"'; } $exampleString = $param['example']; - if (in_array('array', array_keys($param['types']))) { + $useParameterLevelExample = $this->shouldUseParameterLevelExample($param['types'], $exampleString); + if (in_array('array', array_keys($param['types'])) && !$useParameterLevelExample) { // The annotation expects example objects and not arrays, so replace [] with {} $exampleString = str_replace(['[', ']'], ['{', '}'], $exampleString); // Escape quotes differently for the annotation examples $exampleString = str_replace('\"', '""', $exampleString); } + if ($useParameterLevelExample) { + $paramMap[] = 'example="' . $this->normaliseDescriptionText($exampleString) . '"'; + $exampleString = ''; + } $paramMap[] = $this->buildSchemaObjectArrays( $param['types'], strval($param['default']), @@ -2019,4 +2166,18 @@ public function compileOperationLines(string $path, string $opId, string $plugin $this->removeTrailingCommaFromLastLine($lines); return $lines; } + + /** + * Use a parameter-level string example for scalar/array unions so Swagger UI can show a concrete query value. + * + * @param array $typesMap + */ + protected function shouldUseParameterLevelExample(array $typesMap, string $example): bool + { + if (count($typesMap) <= 1 || !array_key_exists('array', $typesMap) || $example === '') { + return false; + } + + return is_array(json_decode($example, true)); + } } diff --git a/config/ParameterExamples.php b/config/ParameterExamples.php new file mode 100644 index 0000000..b2f6693 --- /dev/null +++ b/config/ParameterExamples.php @@ -0,0 +1,599 @@ + [ + 1, + 2, + ], + 'period:\'day\'|\'week\'|\'month\'|\'year\'|\'range\'' => 'day', + 'period:"day"|"week"|"month"|"year"|"range"' => 'day', + 'date:string' => '2026-05-01', + 'segment:string' => 'countryCode==NZ', + 'columns:list|string' => 'example_columns', + 'idSites:int[]|int|string' => 1, + '_hideImplementationData:bool' => true, + '_showAllSegments:bool' => true, + 'idSite:int|string' => 1, + 'apiModule:string' => 'example_apiModule', + 'apiAction:string' => 'example_apiAction', + 'apiParameters:array' => [ + 'example', + ], + 'language:string|false' => 'example_language', + 'period:string|false' => 'day', + 'date:string|false' => '2026-05-01', + 'hideMetricsDoc:bool' => true, + 'showSubtableReports:bool' => true, + 'idSite:int|string|false' => 1, + 'period:string' => 'day', + 'segment:string|false' => 'countryCode==NZ', + 'apiParameters:array|false' => [ + 'example', + ], + 'idGoal:int|string|false' => 1, + 'showTimer:bool' => true, + 'idSubtable:int|string|false' => 1, + 'showRawMetrics:bool' => true, + 'format_metrics:string|null' => 'nb_conversions', + 'idDimension:int|string|false' => 1, + 'columns:string[]|string|false' => 'example_columns', + 'label:string|false' => 'example_label', + 'column:string|false' => 'example_column', + 'legendAppendMetric:bool|string' => true, + 'labelUseAbsoluteUrl:bool|string' => true, + 'labelSeries:string|false' => 'example_labelSeries', + 'showGoalMetricsForGoal:int|string|false' => 10, + 'urls:string[]' => [ + 'example', + ], + 'pluginName:string' => 'Example name', + 'segmentName:string' => 'Example name', + 'idSite:int' => 1, + 'idExperiment:int' => 1, + 'segment:string|null|false' => 'countryCode==NZ', + 'successMetric:string' => 'nb_conversions', + 'name:string' => 'Example name', + 'hypothesis:string' => 'Changing the CTA increases conversions', + 'description:string' => 'Example description', + 'variations:array>' => [ + [ + 'name' => 'Original', + 'percentage' => 50, + ], + [ + 'name' => 'Variation 1', + 'percentage' => 50, + 'redirect_url' => 'https://example.com/variation', + ], + ], + 'includedTargets:array>' => [ + [ + 'attribute' => 'path', + 'type' => 'equals', + 'value' => '/pricing', + ], + ], + 'successMetrics:array>' => [ + [ + 'metric' => 'nb_conversions', + ], + ], + 'confidenceThreshold:int|float|string' => 95.0, + 'mdeRelative:int' => 10, + 'percentageParticipants:int' => 100, + 'excludedTargets:array>' => [ + [ + 'attribute' => 'path', + 'type' => 'equals', + 'value' => '/pricing', + ], + ], + 'startDate:string|false' => '2026-05-01 00:00:00', + 'endDate:string|false' => '2026-05-01 00:00:00', + 'forwardUtmParams:bool' => true, + 'forwardAllQueryParams:bool' => true, + 'statuses:string|array' => [ + 'running', + 'finished', + ], + 'columns:list|string|false' => 'example_columns', + 'expanded:bool' => true, + 'idSubtable:int|null|false' => 1, + 'depth:int|null|false' => 10, + 'flat:bool' => true, + 'pageUrl:string' => 'https://example.com/', + 'pageName:string' => 'Example name', + 'downloadUrl:string' => 'https://example.com/', + 'outlinkUrl:string' => 'https://example.com/', + 'offset:int|string' => 0, + 'limit:int|string' => 100, + 'filterByUserLogin:string|null' => 'example_filterByUserLogin', + 'filterByActivityType:string|null' => 'example_filterByActivityType', + 'period:\'day\'|\'week\'|\'month\'|\'year\'|\'range\'|null' => 'day', + 'period:"day"|"week"|"month"|"year"|"range"|null' => 'day', + 'date:string|null' => '2026-05-01', + 'filterLimit:int|string' => 100, + 'idSite:int|null' => 1, + 'idExport:int' => 1, + 'type:string' => 'example_type', + 'parameters:array' => [ + 'example', + ], + 'note:string' => 'example_note', + 'starred:bool' => true, + 'idNote:int' => 1, + 'note:string|null' => 'example_note', + 'starred:bool|null' => true, + 'idSite:string' => 'example_idSite', + 'lastN:int|null' => 10, + 'getAnnotationText:bool' => true, + 'columns:string|array|false' => [ + 'example', + ], + 'columns:string|string[]|null' => 'example_columns', + 'secondaryDimension:\'pages\'|\'documents\'|null' => 'pages', + 'secondaryDimension:"pages"|"documents"|null' => 'pages', + 'idSubtable:int' => 1, + 'displayDateRange:string' => '2026-05-01', + 'cohorts:string' => 'example_cohorts', + 'filter_limit:int|false' => 100, + 'metric:string|null' => 'nb_conversions', + 'formatMetics:string|int' => 10, + 'idFailure:int|string' => 1, + 'idLogCrashes:int[]|int|string' => 1, + 'idLogCrash:int' => 1, + 'ignore:bool|int' => true, + 'filter_limit:int' => 100, + 'filter_offset:int' => 0, + 'fetchRecentActions:bool|int' => true, + 'filter_sort_column:string' => 'example_filter_sort_column', + 'filter_sort_order:string' => 'example_filter_sort_order', + 'columns:array|string|false' => [ + 'example', + ], + 'filter_pattern:string' => 'example_filter_pattern', + 'expanded:bool|int' => true, + 'flat:bool|int' => true, + 'lastMinutes:int' => 10, + 'idAlert:int' => 1, + 'subPeriodN:int|string' => 10, + 'idSites:string|array' => [ + 1, + 2, + ], + 'ifSuperUserReturnAllAlerts:bool' => true, + 'period:\'day\'|\'week\'|\'month\'' => 'day', + 'period:"day"|"week"|"month"' => 'day', + 'emailMe:bool' => true, + 'additionalEmails:list' => 'user@example.com', + 'phoneNumbers:list' => 'example_phoneNumbers', + 'metric:string' => 'nb_conversions', + 'metricCondition:string' => 'nb_conversions', + 'metricValue:float|int|string' => 1.5, + 'comparedTo:int' => 10, + 'reportUniqueId:string' => 'example_reportUniqueId', + 'reportCondition:false|string' => 'example_reportCondition', + 'reportValue:false|string' => 'example_reportValue', + 'reportMediums:list<\'email\'|\'mobile\'|\'slack\'|\'teams\'>' => 'email', + 'reportMediums:list<"email"|"mobile"|"slack"|"teams">' => 'email', + 'slackChannelID:string' => 'example_slackChannelID', + 'msTeamsWebhookUrl:string' => 'https://example.com/', + 'idDimension:int' => 1, + 'idSubtable:int|false' => 1, + 'scope:\'visit\'|\'action\'' => 'visit', + 'scope:"visit"|"action"' => 'visit', + 'active:bool|int' => true, + 'extractions:array' => [ + 'example', + ], + 'caseSensitive:bool|int' => true, + 'caseSensitive:bool|int|null' => true, + 'idCustomReport:int' => 1, + 'idDestinationSites:int[]' => [ + 'example', + ], + 'reportType:string' => 'example_reportType', + 'metricIds:string[]' => [ + 'nb_conversions', + ], + 'categoryId:string|false' => 'example_categoryId', + 'dimensionIds:string[]' => [ + 'example', + ], + 'subcategoryId:string|false' => 'example_subcategoryId', + 'segmentFilter:string' => 'example_segmentFilter', + 'multipleIdSites:int[]|string[]' => [ + 'example', + ], + 'subCategoryReportIds:int[]' => [ + 'example', + ], + 'skipCategoryMetadata:bool' => true, + 'columns:string|false' => 'example_columns', + 'segment:string|false|null' => 'countryCode==NZ', + '_leavePiwikCoreVariables:bool' => true, + '_leavePriceViewedColumn:bool' => true, + 'login:string' => 'example_login', + 'returnDefaultIfEmpty:bool' => true, + 'dashboardName:string' => 'Example name', + 'addDefaultWidgets:bool' => true, + 'idDashboard:int' => 1, + 'copyToUser:string' => 'example_copyToUser', + 'secondaryDimension:\'eventAction\'|\'eventName\'|false' => 'eventAction', + 'secondaryDimension:"eventAction"|"eventName"|false' => 'eventAction', + 'secondaryDimension:\'eventName\'|\'eventCategory\'|false' => 'eventName', + 'secondaryDimension:"eventName"|"eventCategory"|false' => 'eventName', + 'secondaryDimension:\'eventAction\'|\'eventCategory\'|false' => 'eventAction', + 'secondaryDimension:"eventAction"|"eventCategory"|false' => 'eventAction', + 'featureName:string' => 'Example name', + 'like:bool|null' => true, + 'choice:string|null' => 'example_choice', + 'message:string|null' => 'example_message', + 'question:string' => 'example_question', + 'message:string|false' => 'example_message', + 'matchFormRules:array|false' => [ + 'example', + ], + 'matchPageRules:array|false' => [ + 'example', + ], + 'conversionRuleOption:string' => 'example_conversionRuleOption', + 'conversionRules:array|false' => [ + 'example', + ], + 'idGoal:int|null' => 1, + 'idForm:int' => 1, + 'idForm:int|false' => 1, + 'columns:string|array|false' => [ + 'example', + ], + 'fields:array' => [ + 'example', + ], + 'idFunnel:int|false' => 1, + 'stepPosition:int' => 10, + 'idFunnel:int' => 1, + 'step:string|false' => 'example_step', + 'idGoal:int|string' => 1, + 'isActivated:bool|int|string' => true, + 'steps:array>' => [ + 'example', + ], + 'funnelName:string' => 'Example name', + 'description:?string' => 'Example description', + 'url:string' => 'https://example.com/', + 'idGoal:int' => 1, + 'orderByName:bool' => true, + 'matchAttribute:string' => 'example_matchAttribute', + 'pattern:string' => 'example_pattern', + 'patternType:string' => 'example_patternType', + 'caseSensitive:bool' => true, + 'revenue:bool|float' => true, + 'allowMultipleConversionsPerVisit:bool' => true, + 'useEventValueAsRevenue:bool' => true, + 'abandonedCarts:bool' => true, + 'columns:string|string[]' => [ + 'example', + ], + 'showAllGoalSpecificMetrics:bool' => true, + 'compare:bool' => true, + 'matchPageRules:array' => [ + 'example', + ], + 'sampleLimit:int' => 100, + 'sampleRate:float' => 1.5, + 'excludedElements:string|false' => 'example_excludedElements', + 'screenshotUrl:string|false' => 'https://example.com/', + 'breakpointMobile:int|false' => 10, + 'breakpointTablet:int|false' => 10, + 'captureDomManually:bool' => true, + 'idSiteHsr:int' => 1, + 'minSessionTime:int' => 10, + 'requiresActivity:bool' => true, + 'captureKeystrokes:bool' => true, + 'includePageTreeMirror:bool|int' => true, + 'idLogHsr:int' => 1, + 'idVisit:int' => 1, + 'heatmapType:int' => 10, + 'deviceType:int' => 10, + 'graphType:\'evolution\'|\'verticalBar\'|\'horizontalBar\'|\'pie\'|\'3dPie\'|false' => 'evolution', + 'graphType:"evolution"|"verticalBar"|"horizontalBar"|"pie"|"3dPie"|false' => 'evolution', + 'outputType:int' => 10, + 'labels:string|false' => 'example_labels', + 'showLegend:bool' => true, + 'width:int|false' => 10, + 'height:int|false' => 10, + 'fontSize:int' => 10, + 'legendFontSize:int|false' => 10, + 'aliasedGraph:bool' => true, + 'colors:string|false' => 'example_colors', + 'textColor:string' => 'example_textColor', + 'backgroundColor:string' => 'example_backgroundColor', + 'gridColor:string' => 'example_gridColor', + 'legendAppendMetric:bool' => true, + 'comparedToXPeriods:int' => 10, + 'limitIncreaser:int' => 100, + 'limitDecreaser:int' => 100, + 'filterBy:\'\'|\'movers\'|\'new\'|\'disappeared\'' => '|', + 'filterBy:""|"movers"|"new"|"disappeared"' => '|', + 'minImpactPercent:int' => 10, + 'minGrowthPercent:int' => 10, + 'orderBy:\'absolute\'|\'relative\'|\'importance\'' => 'absolute', + 'orderBy:"absolute"|"relative"|"importance"' => 'absolute', + 'nonce:string' => 'example_nonce', + 'languageCode:string' => 'example_languageCode', + '_ignoreConfig:bool' => true, + 'excludeNonCorePlugins:bool' => true, + 'use12HourClock:bool' => true, + 'idSite:int|int[]' => [ + 1, + 2, + ], + 'showColumns:string|string[]' => [ + 'example', + ], + 'hideColumns:string|string[]' => [ + 'example', + ], + 'period:\'day\'|\'week\'|\'month\'|\'year\'|\'range\'|false' => 'day', + 'period:"day"|"week"|"month"|"year"|"range"|false' => 'day', + 'countVisitorsToFetch:int|false' => 10, + 'minTimestamp:int|false' => 10, + 'doNotFetchActions:bool' => true, + 'enhanced:bool' => true, + 'visitorId:string|false' => 'example_visitorId', + 'limitVisits:int|false' => 100, + 'licenseKey:string' => 'example_licenseKey', + 'columns:string|string[]|false' => 'example_columns', + 'idSubtable:bool|int' => true, + 'secondaryDimension:bool|string' => true, + '_expandAll:bool' => true, + 'provider:string' => 'example_provider', + 'credentials:array' => [ + 'example', + ], + 'phoneNumber:string' => 'example_phoneNumber', + 'verificationCode:string' => 'example_verificationCode', + 'delegatedManagement:bool' => true, + 'isEnabled:bool|int|string' => true, + 'idCampaignDimensionCombination:int|string' => 1, + 'segment:string|null' => 'countryCode==NZ', + '_restrictSitesToLogin:string|null' => 'example__restrictSitesToLogin', + 'pattern:string|null' => 'example_pattern', + 'clientId:string' => 'example_clientId', + 'grantTypes:string[]' => [ + 'example', + ], + 'scope:string' => 'example_scope', + 'redirectUris:string|string[]' => [ + 'example', + ], + 'active:string' => 'example_active', + 'format:string' => 'example_format', + 'plugin:string' => 'example_plugin', + 'visits:array' => [ + 'example', + ], + 'idSites:int|string|int[]' => [ + 1, + 2, + ], + 'anonymizeIp:bool' => true, + 'anonymizeLocation:bool' => true, + 'anonymizeUserId:bool' => true, + 'unsetVisitColumns:string[]' => [ + 'example', + ], + 'unsetLinkVisitActionColumns:string[]' => [ + 'example', + ], + 'passwordConfirmation:string' => 'example_passwordConfirmation', + 'typeReferrer:bool|int|string' => true, + 'idSubtable:int|null' => 1, + '_setReferrerTypeLabel:bool' => true, + 'secondaryDimension:\'entryPageTitle\'|\'entryPageUrl\'|null' => 'entryPageTitle', + 'secondaryDimension:"entryPageTitle"|"entryPageUrl"|null' => 'entryPageTitle', + 'sourceIdSites:array' => [ + 'example', + ], + 'timezone:string' => 'example_timezone', + 'currency:string' => 'example_currency', + 'name:string|null' => 'Example name', + 'sourceIdSites:array|null' => [ + 'example', + ], + 'timezone:string|null' => 'example_timezone', + 'currency:string|null' => 'example_currency', + 'period:\'day\'|\'week\'|\'month\'|\'never\'' => 'day', + 'period:"day"|"week"|"month"|"never"' => 'day', + 'hour:int' => 10, + 'reportFormat:string' => 'example_reportFormat', + 'reports:list' => 'example_reports', + 'parameters:array' => [ + 'example', + ], + 'idSegment:int|false' => 1, + 'evolutionPeriodFor:\'prev\'|\'each\'' => 'prev', + 'evolutionPeriodFor:"prev"|"each"' => 'prev', + 'evolutionPeriodN:int|null' => 10, + 'periodParam:\'day\'|\'week\'|\'month\'|\'year\'|null' => 'day', + 'periodParam:"day"|"week"|"month"|"year"|null' => 'day', + 'idReport:int' => 1, + 'idSite:int|false' => 1, + 'period:\'day\'|\'week\'|\'month\'|\'never\'|false' => 'day', + 'period:"day"|"week"|"month"|"never"|false' => 'day', + 'idReport:int|false' => 1, + 'ifSuperUserReturnOnlySuperUserReports:bool' => true, + 'outputType:int|false' => 10, + 'reportFormat:string|false' => 'example_reportFormat', + 'parameters:array|false' => [ + 'example', + ], + 'period:\'day\'|\'week\'|\'month\'|\'year\'|false' => 'day', + 'period:"day"|"week"|"month"|"year"|false' => 'day', + 'force:bool' => true, + 'idSegment:int' => 1, + 'definition:string' => 'example_definition', + 'autoArchive:bool' => true, + 'enabledAllUsers:bool' => true, + 'piwikUrl:string' => 'https://example.com/', + 'mergeSubdomains:bool' => true, + 'groupPageTitlesByDomain:bool' => true, + 'mergeAliasUrls:bool' => true, + 'visitorCustomVariables:array' => [ + 'example', + ], + 'pageCustomVariables:array' => [ + 'example', + ], + 'customCampaignNameQueryParam:string' => 'Example name', + 'customCampaignKeywordParam:string' => 'example_customCampaignKeywordParam', + 'doNotTrack:bool' => true, + 'disableCookies:bool' => true, + 'trackNoScript:bool' => true, + 'crossDomain:bool' => true, + 'forceMatomoEndpoint:bool' => true, + 'excludedQueryParams:string|string[]' => [ + 'example', + ], + 'excludedReferrers:string|string[]' => [ + 'example', + ], + 'disableCampaignParameters:bool' => true, + 'actionName:string|false' => 'Example name', + 'idGoal:int|false' => 1, + 'revenue:int|float|false' => 1.5, + 'group:string' => 'example_group', + 'fetchAliasUrls:bool' => true, + 'pattern:string|false' => 'example_pattern', + 'limit:int|false' => 100, + 'sitesToExclude:int[]' => [ + 'example', + ], + 'permission:string' => 'example_permission', + 'limit:int|null' => 100, + 'siteTypesToExclude:string[]' => [ + 'example', + ], + '_restrictSitesToLogin:string|false' => 'example__restrictSitesToLogin', + 'siteName:string' => 'Example name', + 'urls:string[]|string|null' => 'https://example.com/', + 'ecommerce:int|null' => 10, + 'siteSearch:int|null' => 10, + 'searchKeywordParameters:string|null' => 'example_searchKeywordParameters', + 'searchCategoryParameters:string|null' => 'example_searchCategoryParameters', + 'excludedIps:string|null' => 'example_excludedIps', + 'excludedQueryParameters:string|null' => 'example_excludedQueryParameters', + 'group:string|null' => 'example_group', + 'startDate:string|null' => '2026-05-01 00:00:00', + 'excludedUserAgents:string|null' => 'example_excludedUserAgents', + 'keepURLFragments:int|null' => 10, + 'type:string|null' => 'example_type', + 'settingValues:SettingValues|null' => 'example_settingValues', + 'excludeUnknownUrls:bool|null' => true, + 'excludedReferrers:string|null' => 'example_excludedReferrers', + 'passwordConfirmation:string|null' => 'example_passwordConfirmation', + 'urls:string[]|string' => 'https://example.com/', + 'ipRange:string' => 'example_ipRange', + 'excludedIps:string' => 'example_excludedIps', + 'searchKeywordParameters:string' => 'example_searchKeywordParameters', + 'searchCategoryParameters:string' => 'example_searchCategoryParameters', + 'excludedUserAgents:string' => 'example_excludedUserAgents', + 'excludedReferrers:string' => 'example_excludedReferrers', + 'enabled:bool' => true, + 'defaultCurrency:string' => 'example_defaultCurrency', + 'defaultTimezone:string' => 'example_defaultTimezone', + 'exclusionType:string' => 'example_exclusionType', + 'queryParamsToExclude:string|null' => 'example_queryParamsToExclude', + 'siteName:string|null' => 'Example name', + 'countryCode:string|null' => 'example_countryCode', + 'multipleTimezonesInCountry:bool|null' => true, + 'oldGroupName:string' => 'Example name', + 'newGroupName:string' => 'Example name', + 'idContext:string' => 'example_idContext', + 'idContainer:string' => 'example_idContainer', + 'environment:string' => 'example_environment', + 'jsFramework:string' => 'example_jsFramework', + 'idContainerVersion:int' => 1, + 'fireTriggerIds:int[]' => [ + 'example', + ], + 'blockTriggerIds:int[]' => [ + 'example', + ], + 'fireLimit:string' => 'example_fireLimit', + 'fireDelay:int' => 10, + 'priority:int' => 10, + 'endDate:string|null' => '2026-05-01 00:00:00', + 'description:string|null' => 'Example description', + 'status:string' => 'running', + 'idTag:int' => 1, + 'idTrigger:int' => 1, + 'conditions:array>' => [ + 'example', + ], + 'idVariable:int' => 1, + 'defaultValue:bool|float|int|string|null' => true, + 'lookupTable:array>' => [ + 'example', + ], + 'context:string' => 'example_context', + 'ignoreGtmDataLayer:int' => 10, + 'isTagFireLimitAllowedInPreviewMode:int' => 100, + 'activelySyncGtmDataLayer:int' => 10, + 'idContainerVersion:int|null' => 1, + 'exportedContainerVersion:string' => 'example_exportedContainerVersion', + 'backupName:string' => 'Example name', + '_isDraftRestoreCall:bool' => true, + 'id:string' => 'example_id', + 'pageTitle:string' => 'example_pageTitle', + 'limitBeforeGrouping:int|string' => 100, + 'actionName:string' => 'Example name', + 'actionType:\'url\'|\'title\'' => 'url', + 'actionType:"url"|"title"' => 'url', + 'parts:string' => 'example_parts', + 'userLogin:string' => 'example_userLogin', + 'ip:string|false' => 'example_ip', + 'provider:string|false' => 'example_provider', + 'providerId:string' => 'example_providerId', + 'dataSource:string|false' => 'example_dataSource', + 'limitActionsPerStep:int' => 100, + 'exploreStep:int|false' => 10, + 'exploreUrl:string|false' => 'https://example.com/', + 'interactionPosition:string' => 'example_interactionPosition', + 'offsetActionsPerStep:int|false' => 0, + 'preferenceName:string' => 'Example name', + 'preferenceValue:mixed' => 'example_preferenceValue', + 'userLogin:string|null|false' => 'example_userLogin', + 'offset:int|null' => 0, + 'filter_search:string|null' => 'example_filter_search', + 'filter_access:string|null' => 'example_filter_access', + 'filter_status:string|null' => 'running', + 'userLogins:string' => 'example_userLogins', + 'access:string' => 'example_access', + 'userEmail:string' => 'user@example.com', + 'password:string' => 'example_password', + 'email:string' => 'user@example.com', + '_isPasswordHashed:bool' => true, + 'initialIdSite:int|null' => 10, + 'expiryInDays:int|null' => 10, + 'hasSuperUserAccess:bool|int|string' => true, + 'password:string|false' => 'example_password', + 'email:string|false' => 'user@example.com', + 'passwordConfirmation:string|false' => 'example_passwordConfirmation', + 'access:string|list' => 'example_access', + 'idSites:string|int|int[]' => [ + 1, + 2, + ], + 'capabilities:string|string[]' => [ + 'example', + ], + 'idSites:int|int[]|string' => 1, + 'expireDate:string|null' => '2026-05-01', + 'expireHours:int|string' => 10, + 'secureOnly:bool' => true, + 'expiryInDays:int' => 10, + 'columns:list|string|null' => 'example_columns', + 'hideFutureHoursWhenToday:bool' => true, +]; \ No newline at end of file diff --git a/tests/Resources/MockAnnotationGenerator.php b/tests/Resources/MockAnnotationGenerator.php index b56f7b1..31b1bd9 100644 --- a/tests/Resources/MockAnnotationGenerator.php +++ b/tests/Resources/MockAnnotationGenerator.php @@ -90,4 +90,25 @@ public function determineResponses(array $rules, string $plugin, string $method, { return parent::determineResponses($rules, $plugin, $method, $reflectionMethod, $paramsData); } + + public function normaliseConfiguredParameterExample($example, array $typesMap = []): ?string + { + return parent::normaliseConfiguredParameterExample($example, $typesMap); + } + + public function isBasicExampleArray(array $example): bool + { + return parent::isBasicExampleArray($example); + } + + public function supportsBasicArrayExample(array $typesMap): bool + { + return parent::supportsBasicArrayExample($typesMap); + } + + public function shouldUseParameterLevelExample(array $typesMap, string $example): bool + { + return parent::shouldUseParameterLevelExample($typesMap, $example); + } + } diff --git a/tests/Unit/AnnotationGeneratorTest.php b/tests/Unit/AnnotationGeneratorTest.php index d91dffd..9e3e7da 100644 --- a/tests/Unit/AnnotationGeneratorTest.php +++ b/tests/Unit/AnnotationGeneratorTest.php @@ -688,7 +688,7 @@ public function getTestDataForBuildParameterAnnotationData(): iterable 'description' => '', 'required' => 'true', 'default' => 'Piwik\API\NoDefaultValue', - 'example' => '', + 'example' => 'day', 'enum' => ['day', 'week', 'month'], ]]; yield 'should extract enum values when docInfo uses double-quoted string literals' => ['format', [], [ @@ -724,6 +724,26 @@ public function getTestDataForBuildParameterAnnotationData(): iterable 'default' => 'Piwik\API\NoDefaultValue', 'example' => '', ]]; + yield 'should allow union with generic string array type' => ['statuses', [], [ + 'type' => 'string|array', + ], [ + 'name' => 'statuses', + 'types' => ['string' => null, 'array' => 'string'], + 'description' => '', + 'required' => 'true', + 'default' => 'Piwik\API\NoDefaultValue', + 'example' => '["running","finished"]', + ]]; + yield 'should determine subtype for list syntax' => ['someParam', [], [ + 'type' => 'list', + ], [ + 'name' => 'someParam', + 'types' => ['array' => 'string'], + 'description' => '', + 'required' => 'true', + 'default' => 'Piwik\API\NoDefaultValue', + 'example' => '', + ]]; yield 'should allow multiple types when metadata type is bool and doc type is piped' => ['someParam', [ 'type' => 'bool', ], [ @@ -821,6 +841,53 @@ public function getTestDataForBuildParameterAnnotationData(): iterable 'default' => 'Piwik\API\NoDefaultValue', 'example' => '[{"key1":"value1","key2":"value2"},{"key3":"value3","key4":"value4"}]', ]]; + yield 'should use config example when docblock example is absent' => ['idSite', [ + 'type' => 'string', + ], [ + 'type' => 'int|string', + ], [ + 'name' => 'idSite', + 'types' => ['integer' => null, 'string' => null], + 'description' => '', + 'required' => 'true', + 'default' => 'Piwik\API\NoDefaultValue', + 'example' => '1', + ]]; + yield 'should not use config example when parameter is optional' => ['statuses', [ + 'default' => [], + ], [ + 'type' => 'string|array', + ], [ + 'name' => 'statuses', + 'types' => ['string' => null, 'array' => 'string'], + 'description' => '', + 'required' => 'false', + 'default' => '[]', + 'example' => '', + ]]; + yield 'should prefer docblock example over config example' => ['idSite', [ + 'type' => 'string', + ], [ + 'type' => 'int|string', + 'description' => 'Some test description. [@example=99]', + ], [ + 'name' => 'idSite', + 'types' => ['integer' => null, 'string' => null], + 'description' => 'Some test description.', + 'required' => 'true', + 'default' => 'Piwik\API\NoDefaultValue', + 'example' => '99', + ]]; + yield 'should preserve integer and integer array union type' => ['idSites', [], [ + 'type' => 'int|int[]', + ], [ + 'name' => 'idSites', + 'types' => ['integer' => null, 'array' => 'integer'], + 'description' => '', + 'required' => 'true', + 'default' => 'Piwik\API\NoDefaultValue', + 'example' => '', + ]]; } public function testDetermineParameters(): void @@ -1220,6 +1287,28 @@ public function testBuildSchemaObjectArrayIgnoresEnumForNonStringTypes(): void $this->assertEquals($expectedWithoutEnum, $this->annotationGenerator->buildSchemaObjectArray('integer', '', NoDefaultValue::class, '1', ['1', '2'])); } + public function testNormaliseConfiguredParameterExampleSupportsOnlySimpleValues(): void + { + $annotationGenerator = new MockAnnotationGenerator(new DocumentationGenerator()); + + $this->assertSame('true', $annotationGenerator->normaliseConfiguredParameterExample(true)); + $this->assertSame('1.5', $annotationGenerator->normaliseConfiguredParameterExample(1.5)); + $this->assertSame('["one","two"]', $annotationGenerator->normaliseConfiguredParameterExample(['one', 'two'], ['array' => 'string'])); + $this->assertNull($annotationGenerator->normaliseConfiguredParameterExample(['one', 'two'], ['string' => null])); + $this->assertSame('["one","two"]', $annotationGenerator->normaliseConfiguredParameterExample(['one', 'two'], ['array' => 'string', 'string' => null])); + $this->assertNull($annotationGenerator->normaliseConfiguredParameterExample(['key' => 'value'], ['array' => 'string'])); + $this->assertNull($annotationGenerator->normaliseConfiguredParameterExample([['nested']], ['array' => 'string'])); + } + + public function testShouldUseParameterLevelExampleForScalarArrayUnions(): void + { + $annotationGenerator = new MockAnnotationGenerator(new DocumentationGenerator()); + + $this->assertTrue($annotationGenerator->shouldUseParameterLevelExample(['string' => null, 'array' => 'string'], '["one","two"]')); + $this->assertFalse($annotationGenerator->shouldUseParameterLevelExample(['array' => 'string'], '["one","two"]')); + $this->assertFalse($annotationGenerator->shouldUseParameterLevelExample(['string' => null, 'array' => 'string'], 'one')); + } + /** * @dataProvider getTestDataForWrapStringWithQuotes * @@ -1243,6 +1332,7 @@ public function getTestDataForWrapStringWithQuotes(): iterable yield 'should be empty quoted string if everything is empty' => ['', '', null, '""']; yield 'should be empty quoted string if string type and empty value' => ['', 'string', null, '""']; yield 'should be empty string if integer type and empty value' => ['', 'integer', null, '']; + yield 'should be empty string if number type and empty value' => ['', 'number', null, '']; yield 'should be empty string if boolean type and empty value' => ['', 'boolean', null, '']; yield 'should be empty string if array type and empty value' => ['', 'array', null, '']; yield 'should be empty quoted string if string type and quoted empty string value' => ['""', 'string', null, '""']; @@ -1253,6 +1343,7 @@ public function getTestDataForWrapStringWithQuotes(): iterable yield 'should be quoted string if no type and string value' => ['test', '', null, '"test"']; yield 'should be quoted string if string type and string value' => ['test', 'string', null, '"test"']; yield 'should be integer string if integer type and integer string value' => ['12', 'integer', null, '12']; + yield 'should be number string if number type and float string value' => ['1.5', 'number', null, '1.5']; yield 'should be boolean string if boolean type and boolean string value' => ['true', 'boolean', null, 'true']; yield 'should be array string if array type and array string value' => ['[]', 'array', null, '[]']; yield 'should be use the custom quote character when provided even when not quote' => ['test', 'string', '|', "|test|"]; From a2cdabe26a89e5b987e35b797e759c96c7832306 Mon Sep 17 00:00:00 2001 From: Lachlan Reynolds Date: Fri, 15 May 2026 11:26:48 +1200 Subject: [PATCH 2/5] generated more meaningful examples + removed not required --- config/ParameterExamples.php | 677 ++++++++--------------------------- 1 file changed, 156 insertions(+), 521 deletions(-) diff --git a/config/ParameterExamples.php b/config/ParameterExamples.php index b2f6693..883b57d 100644 --- a/config/ParameterExamples.php +++ b/config/ParameterExamples.php @@ -1,599 +1,234 @@ [ - 1, - 2, - ], + 'idSite:int|string|int[]' => 1, 'period:\'day\'|\'week\'|\'month\'|\'year\'|\'range\'' => 'day', 'period:"day"|"week"|"month"|"year"|"range"' => 'day', 'date:string' => '2026-05-01', - 'segment:string' => 'countryCode==NZ', - 'columns:list|string' => 'example_columns', - 'idSites:int[]|int|string' => 1, - '_hideImplementationData:bool' => true, - '_showAllSegments:bool' => true, 'idSite:int|string' => 1, - 'apiModule:string' => 'example_apiModule', - 'apiAction:string' => 'example_apiAction', - 'apiParameters:array' => [ - 'example', - ], - 'language:string|false' => 'example_language', - 'period:string|false' => 'day', - 'date:string|false' => '2026-05-01', - 'hideMetricsDoc:bool' => true, - 'showSubtableReports:bool' => true, - 'idSite:int|string|false' => 1, + 'apiModule:string' => 'VisitsSummary', + 'apiAction:string' => 'get', 'period:string' => 'day', - 'segment:string|false' => 'countryCode==NZ', - 'apiParameters:array|false' => [ - 'example', - ], - 'idGoal:int|string|false' => 1, - 'showTimer:bool' => true, - 'idSubtable:int|string|false' => 1, - 'showRawMetrics:bool' => true, - 'format_metrics:string|null' => 'nb_conversions', - 'idDimension:int|string|false' => 1, - 'columns:string[]|string|false' => 'example_columns', - 'label:string|false' => 'example_label', - 'column:string|false' => 'example_column', - 'legendAppendMetric:bool|string' => true, - 'labelUseAbsoluteUrl:bool|string' => true, - 'labelSeries:string|false' => 'example_labelSeries', - 'showGoalMetricsForGoal:int|string|false' => 10, 'urls:string[]' => [ - 'example', + 'https://example.org', + 'https://example.org/pricing', ], - 'pluginName:string' => 'Example name', - 'segmentName:string' => 'Example name', + 'pluginName:string' => 'Goals', + 'segmentName:string' => 'New Zealand visitors', 'idSite:int' => 1, 'idExperiment:int' => 1, - 'segment:string|null|false' => 'countryCode==NZ', - 'successMetric:string' => 'nb_conversions', - 'name:string' => 'Example name', - 'hypothesis:string' => 'Changing the CTA increases conversions', - 'description:string' => 'Example description', + 'successMetric:string' => 'conversion_rate', + 'name:string' => 'Pricing page signup test', + 'hypothesis:string' => 'A shorter signup flow will improve conversions.', + 'description:string' => 'Compare the pricing page signup experience for New Zealand traffic.', 'variations:array>' => [ [ - 'name' => 'Original', + 'name' => 'Original pricing page', 'percentage' => 50, ], [ - 'name' => 'Variation 1', + 'name' => 'Short signup form', 'percentage' => 50, - 'redirect_url' => 'https://example.com/variation', ], ], 'includedTargets:array>' => [ [ - 'attribute' => 'path', + 'attribute' => 'url', 'type' => 'equals', - 'value' => '/pricing', + 'value' => 'https://example.org/pricing', ], ], 'successMetrics:array>' => [ [ - 'metric' => 'nb_conversions', + 'metric' => 'conversion_rate', ], ], - 'confidenceThreshold:int|float|string' => 95.0, + 'confidenceThreshold:int|float|string' => 95, 'mdeRelative:int' => 10, 'percentageParticipants:int' => 100, - 'excludedTargets:array>' => [ - [ - 'attribute' => 'path', - 'type' => 'equals', - 'value' => '/pricing', - ], - ], - 'startDate:string|false' => '2026-05-01 00:00:00', - 'endDate:string|false' => '2026-05-01 00:00:00', - 'forwardUtmParams:bool' => true, - 'forwardAllQueryParams:bool' => true, 'statuses:string|array' => [ 'running', 'finished', ], - 'columns:list|string|false' => 'example_columns', - 'expanded:bool' => true, - 'idSubtable:int|null|false' => 1, - 'depth:int|null|false' => 10, - 'flat:bool' => true, - 'pageUrl:string' => 'https://example.com/', - 'pageName:string' => 'Example name', - 'downloadUrl:string' => 'https://example.com/', - 'outlinkUrl:string' => 'https://example.com/', - 'offset:int|string' => 0, - 'limit:int|string' => 100, - 'filterByUserLogin:string|null' => 'example_filterByUserLogin', - 'filterByActivityType:string|null' => 'example_filterByActivityType', - 'period:\'day\'|\'week\'|\'month\'|\'year\'|\'range\'|null' => 'day', - 'period:"day"|"week"|"month"|"year"|"range"|null' => 'day', - 'date:string|null' => '2026-05-01', - 'filterLimit:int|string' => 100, - 'idSite:int|null' => 1, - 'idExport:int' => 1, - 'type:string' => 'example_type', - 'parameters:array' => [ - 'example', - ], - 'note:string' => 'example_note', - 'starred:bool' => true, - 'idNote:int' => 1, - 'note:string|null' => 'example_note', - 'starred:bool|null' => true, - 'idSite:string' => 'example_idSite', - 'lastN:int|null' => 10, - 'getAnnotationText:bool' => true, - 'columns:string|array|false' => [ - 'example', - ], - 'columns:string|string[]|null' => 'example_columns', - 'secondaryDimension:\'pages\'|\'documents\'|null' => 'pages', - 'secondaryDimension:"pages"|"documents"|null' => 'pages', - 'idSubtable:int' => 1, - 'displayDateRange:string' => '2026-05-01', - 'cohorts:string' => 'example_cohorts', - 'filter_limit:int|false' => 100, - 'metric:string|null' => 'nb_conversions', - 'formatMetics:string|int' => 10, - 'idFailure:int|string' => 1, - 'idLogCrashes:int[]|int|string' => 1, - 'idLogCrash:int' => 1, - 'ignore:bool|int' => true, - 'filter_limit:int' => 100, - 'filter_offset:int' => 0, - 'fetchRecentActions:bool|int' => true, - 'filter_sort_column:string' => 'example_filter_sort_column', - 'filter_sort_order:string' => 'example_filter_sort_order', - 'columns:array|string|false' => [ - 'example', - ], - 'filter_pattern:string' => 'example_filter_pattern', - 'expanded:bool|int' => true, - 'flat:bool|int' => true, - 'lastMinutes:int' => 10, + 'pageUrl:string' => 'https://example.org/pricing', + 'pageName:string' => 'Pricing', + 'downloadUrl:string' => 'https://example.org/files/brochure.pdf', + 'outlinkUrl:string' => 'https://partner.example.com/signup', + 'idExport:int' => 7, + 'type:string' => 'html', + 'parameters:string' => 'module=API&method=VisitsSummary.get&idSite=1&period=day&date=2026-05-01&format=html', + 'note:string' => 'Investigate conversion spike from NZ campaign traffic.', + 'idNote:int' => 12, + 'idSite:string' => '1', + 'idSubtable:int' => 2, + 'displayDateRange:string' => '2026-05-01,2026-05-07', + 'cohorts:string' => '2026-05-01,2026-05-07', + 'idFailure:int|string' => 3, + 'idLogCrashes:int[]|int|string' => [101, 102], + 'idLogCrash:int' => 101, 'idAlert:int' => 1, - 'subPeriodN:int|string' => 10, + 'subPeriodN:int|string' => 1, 'idSites:string|array' => [ 1, 2, ], - 'ifSuperUserReturnAllAlerts:bool' => true, - 'period:\'day\'|\'week\'|\'month\'' => 'day', - 'period:"day"|"week"|"month"' => 'day', + 'period:\'day\'|\'week\'|\'month\'' => 'week', + 'period:"day"|"week"|"month"' => 'week', 'emailMe:bool' => true, - 'additionalEmails:list' => 'user@example.com', - 'phoneNumbers:list' => 'example_phoneNumbers', - 'metric:string' => 'nb_conversions', + 'additionalEmails:list' => [ + 'alice@example.org', + ], + 'phoneNumbers:list' => [ + '+64211234567', + ], + 'metric:string' => 'nb_visits', 'metricCondition:string' => 'nb_conversions', - 'metricValue:float|int|string' => 1.5, - 'comparedTo:int' => 10, - 'reportUniqueId:string' => 'example_reportUniqueId', - 'reportCondition:false|string' => 'example_reportCondition', - 'reportValue:false|string' => 'example_reportValue', - 'reportMediums:list<\'email\'|\'mobile\'|\'slack\'|\'teams\'>' => 'email', - 'reportMediums:list<"email"|"mobile"|"slack"|"teams">' => 'email', - 'slackChannelID:string' => 'example_slackChannelID', - 'msTeamsWebhookUrl:string' => 'https://example.com/', + 'metricValue:float|int|string' => 10, + 'comparedTo:int' => 1, + 'reportUniqueId:string' => 'VisitsSummary_get', 'idDimension:int' => 1, - 'idSubtable:int|false' => 1, - 'scope:\'visit\'|\'action\'' => 'visit', - 'scope:"visit"|"action"' => 'visit', + 'scope:\'visit\'|\'action\'' => 'action', + 'scope:"visit"|"action"' => 'action', 'active:bool|int' => true, - 'extractions:array' => [ - 'example', - ], - 'caseSensitive:bool|int' => true, - 'caseSensitive:bool|int|null' => true, 'idCustomReport:int' => 1, - 'idDestinationSites:int[]' => [ - 'example', - ], - 'reportType:string' => 'example_reportType', + 'reportType:string' => 'table', 'metricIds:string[]' => [ + 'nb_visits', 'nb_conversions', + 'conversion_rate', ], - 'categoryId:string|false' => 'example_categoryId', - 'dimensionIds:string[]' => [ - 'example', - ], - 'subcategoryId:string|false' => 'example_subcategoryId', - 'segmentFilter:string' => 'example_segmentFilter', - 'multipleIdSites:int[]|string[]' => [ - 'example', - ], - 'subCategoryReportIds:int[]' => [ - 'example', - ], - 'skipCategoryMetadata:bool' => true, - 'columns:string|false' => 'example_columns', - 'segment:string|false|null' => 'countryCode==NZ', - '_leavePiwikCoreVariables:bool' => true, - '_leavePriceViewedColumn:bool' => true, - 'login:string' => 'example_login', - 'returnDefaultIfEmpty:bool' => true, - 'dashboardName:string' => 'Example name', - 'addDefaultWidgets:bool' => true, + 'idSubtable:int|string|false' => 1, + 'login:string' => 'alice', 'idDashboard:int' => 1, - 'copyToUser:string' => 'example_copyToUser', - 'secondaryDimension:\'eventAction\'|\'eventName\'|false' => 'eventAction', - 'secondaryDimension:"eventAction"|"eventName"|false' => 'eventAction', - 'secondaryDimension:\'eventName\'|\'eventCategory\'|false' => 'eventName', - 'secondaryDimension:"eventName"|"eventCategory"|false' => 'eventName', - 'secondaryDimension:\'eventAction\'|\'eventCategory\'|false' => 'eventAction', - 'secondaryDimension:"eventAction"|"eventCategory"|false' => 'eventAction', - 'featureName:string' => 'Example name', - 'like:bool|null' => true, - 'choice:string|null' => 'example_choice', - 'message:string|null' => 'example_message', - 'question:string' => 'example_question', - 'message:string|false' => 'example_message', - 'matchFormRules:array|false' => [ - 'example', - ], - 'matchPageRules:array|false' => [ - 'example', - ], - 'conversionRuleOption:string' => 'example_conversionRuleOption', - 'conversionRules:array|false' => [ - 'example', - ], - 'idGoal:int|null' => 1, + 'copyToUser:string' => 'alice', + 'featureName:string' => 'FormAnalytics', + 'question:string' => 'Which pricing page sections get the most engagement?', 'idForm:int' => 1, - 'idForm:int|false' => 1, - 'columns:string|array|false' => [ - 'example', - ], - 'fields:array' => [ - 'example', - ], - 'idFunnel:int|false' => 1, - 'stepPosition:int' => 10, + 'lastMinutes:int' => 30, + 'stepPosition:int' => 1, 'idFunnel:int' => 1, - 'step:string|false' => 'example_step', 'idGoal:int|string' => 1, 'isActivated:bool|int|string' => true, + 'funnelName:string' => 'Pricing Signup Funnel', 'steps:array>' => [ - 'example', + [ + 'name' => 'Pricing Page', + 'url' => 'https://example.org/pricing', + ], + [ + 'name' => 'Signup Page', + 'url' => 'https://partner.example.com/signup', + ], ], - 'funnelName:string' => 'Example name', - 'description:?string' => 'Example description', - 'url:string' => 'https://example.com/', + 'url:string' => 'https://example.org/pricing', 'idGoal:int' => 1, - 'orderByName:bool' => true, - 'matchAttribute:string' => 'example_matchAttribute', - 'pattern:string' => 'example_pattern', - 'patternType:string' => 'example_patternType', - 'caseSensitive:bool' => true, - 'revenue:bool|float' => true, - 'allowMultipleConversionsPerVisit:bool' => true, - 'useEventValueAsRevenue:bool' => true, - 'abandonedCarts:bool' => true, - 'columns:string|string[]' => [ - 'example', - ], - 'showAllGoalSpecificMetrics:bool' => true, - 'compare:bool' => true, + 'matchAttribute:string' => 'url', + 'pattern:string' => 'https://example.org/pricing', + 'patternType:string' => 'equals', 'matchPageRules:array' => [ - 'example', + [ + 'attribute' => 'url', + 'type' => 'equals', + 'value' => 'https://example.org/pricing', + ], ], - 'sampleLimit:int' => 100, - 'sampleRate:float' => 1.5, - 'excludedElements:string|false' => 'example_excludedElements', - 'screenshotUrl:string|false' => 'https://example.com/', - 'breakpointMobile:int|false' => 10, - 'breakpointTablet:int|false' => 10, - 'captureDomManually:bool' => true, 'idSiteHsr:int' => 1, - 'minSessionTime:int' => 10, - 'requiresActivity:bool' => true, - 'captureKeystrokes:bool' => true, - 'includePageTreeMirror:bool|int' => true, - 'idLogHsr:int' => 1, - 'idVisit:int' => 1, - 'heatmapType:int' => 10, - 'deviceType:int' => 10, - 'graphType:\'evolution\'|\'verticalBar\'|\'horizontalBar\'|\'pie\'|\'3dPie\'|false' => 'evolution', - 'graphType:"evolution"|"verticalBar"|"horizontalBar"|"pie"|"3dPie"|false' => 'evolution', - 'outputType:int' => 10, - 'labels:string|false' => 'example_labels', - 'showLegend:bool' => true, - 'width:int|false' => 10, - 'height:int|false' => 10, - 'fontSize:int' => 10, - 'legendFontSize:int|false' => 10, - 'aliasedGraph:bool' => true, - 'colors:string|false' => 'example_colors', - 'textColor:string' => 'example_textColor', - 'backgroundColor:string' => 'example_backgroundColor', - 'gridColor:string' => 'example_gridColor', - 'legendAppendMetric:bool' => true, - 'comparedToXPeriods:int' => 10, - 'limitIncreaser:int' => 100, - 'limitDecreaser:int' => 100, - 'filterBy:\'\'|\'movers\'|\'new\'|\'disappeared\'' => '|', - 'filterBy:""|"movers"|"new"|"disappeared"' => '|', - 'minImpactPercent:int' => 10, - 'minGrowthPercent:int' => 10, - 'orderBy:\'absolute\'|\'relative\'|\'importance\'' => 'absolute', - 'orderBy:"absolute"|"relative"|"importance"' => 'absolute', - 'nonce:string' => 'example_nonce', - 'languageCode:string' => 'example_languageCode', - '_ignoreConfig:bool' => true, - 'excludeNonCorePlugins:bool' => true, + 'idLogHsr:int' => 101, + 'idVisit:int' => 1001, + 'heatmapType:int' => 1, + 'deviceType:int' => 1, + 'languageCode:string' => 'en', 'use12HourClock:bool' => true, - 'idSite:int|int[]' => [ - 1, - 2, - ], - 'showColumns:string|string[]' => [ - 'example', - ], - 'hideColumns:string|string[]' => [ - 'example', - ], - 'period:\'day\'|\'week\'|\'month\'|\'year\'|\'range\'|false' => 'day', - 'period:"day"|"week"|"month"|"year"|"range"|false' => 'day', - 'countVisitorsToFetch:int|false' => 10, - 'minTimestamp:int|false' => 10, - 'doNotFetchActions:bool' => true, - 'enhanced:bool' => true, - 'visitorId:string|false' => 'example_visitorId', - 'limitVisits:int|false' => 100, - 'licenseKey:string' => 'example_licenseKey', - 'columns:string|string[]|false' => 'example_columns', - 'idSubtable:bool|int' => true, - 'secondaryDimension:bool|string' => true, - '_expandAll:bool' => true, - 'provider:string' => 'example_provider', - 'credentials:array' => [ - 'example', - ], - 'phoneNumber:string' => 'example_phoneNumber', - 'verificationCode:string' => 'example_verificationCode', + 'idSite:int|int[]' => 1, + 'licenseKey:string' => 'hsr-demo-license', + 'provider:string' => 'google', + 'phoneNumber:string' => '+64211234567', + 'verificationCode:string' => '123456', 'delegatedManagement:bool' => true, 'isEnabled:bool|int|string' => true, - 'idCampaignDimensionCombination:int|string' => 1, - 'segment:string|null' => 'countryCode==NZ', - '_restrictSitesToLogin:string|null' => 'example__restrictSitesToLogin', - 'pattern:string|null' => 'example_pattern', - 'clientId:string' => 'example_clientId', - 'grantTypes:string[]' => [ - 'example', - ], - 'scope:string' => 'example_scope', - 'redirectUris:string|string[]' => [ - 'example', - ], - 'active:string' => 'example_active', - 'format:string' => 'example_format', - 'plugin:string' => 'example_plugin', + 'clientId:string' => 'analytics-dashboard', + 'grantTypes:string[]' => ['authorization_code'], + 'scope:string' => 'read:reports', + 'active:string' => '1', + 'plugin:string' => 'HeatmapSessionRecording', + 'format:string' => 'html', 'visits:array' => [ - 'example', + [ + 'idVisit' => 12345, + 'idSite' => 1, + ], ], + 'segment:string' => 'countryCode==NZ', 'idSites:int|string|int[]' => [ 1, 2, ], - 'anonymizeIp:bool' => true, - 'anonymizeLocation:bool' => true, - 'anonymizeUserId:bool' => true, - 'unsetVisitColumns:string[]' => [ - 'example', - ], - 'unsetLinkVisitActionColumns:string[]' => [ - 'example', - ], - 'passwordConfirmation:string' => 'example_passwordConfirmation', - 'typeReferrer:bool|int|string' => true, - 'idSubtable:int|null' => 1, - '_setReferrerTypeLabel:bool' => true, - 'secondaryDimension:\'entryPageTitle\'|\'entryPageUrl\'|null' => 'entryPageTitle', - 'secondaryDimension:"entryPageTitle"|"entryPageUrl"|null' => 'entryPageTitle', 'sourceIdSites:array' => [ - 'example', + 1, + 2, ], - 'timezone:string' => 'example_timezone', - 'currency:string' => 'example_currency', - 'name:string|null' => 'Example name', - 'sourceIdSites:array|null' => [ - 'example', + 'timezone:string' => 'Pacific/Auckland', + 'currency:string' => 'NZD', + 'period:\'day\'|\'week\'|\'month\'|\'never\'' => 'never', + 'period:"day"|"week"|"month"|"never"' => 'never', + 'hour:int' => 9, + 'reportFormat:string' => 'pdf', + 'reports:list' => [ + 'VisitsSummary.get', + 'Goals.get', ], - 'timezone:string|null' => 'example_timezone', - 'currency:string|null' => 'example_currency', - 'period:\'day\'|\'week\'|\'month\'|\'never\'' => 'day', - 'period:"day"|"week"|"month"|"never"' => 'day', - 'hour:int' => 10, - 'reportFormat:string' => 'example_reportFormat', - 'reports:list' => 'example_reports', 'parameters:array' => [ - 'example', + 'module' => 'VisitsSummary', + 'action' => 'get', ], - 'idSegment:int|false' => 1, - 'evolutionPeriodFor:\'prev\'|\'each\'' => 'prev', - 'evolutionPeriodFor:"prev"|"each"' => 'prev', - 'evolutionPeriodN:int|null' => 10, - 'periodParam:\'day\'|\'week\'|\'month\'|\'year\'|null' => 'day', - 'periodParam:"day"|"week"|"month"|"year"|null' => 'day', 'idReport:int' => 1, - 'idSite:int|false' => 1, - 'period:\'day\'|\'week\'|\'month\'|\'never\'|false' => 'day', - 'period:"day"|"week"|"month"|"never"|false' => 'day', - 'idReport:int|false' => 1, - 'ifSuperUserReturnOnlySuperUserReports:bool' => true, - 'outputType:int|false' => 10, - 'reportFormat:string|false' => 'example_reportFormat', - 'parameters:array|false' => [ - 'example', - ], - 'period:\'day\'|\'week\'|\'month\'|\'year\'|false' => 'day', - 'period:"day"|"week"|"month"|"year"|false' => 'day', - 'force:bool' => true, + 'idSite:int|null' => 1, 'idSegment:int' => 1, - 'definition:string' => 'example_definition', - 'autoArchive:bool' => true, - 'enabledAllUsers:bool' => true, - 'piwikUrl:string' => 'https://example.com/', - 'mergeSubdomains:bool' => true, - 'groupPageTitlesByDomain:bool' => true, - 'mergeAliasUrls:bool' => true, - 'visitorCustomVariables:array' => [ - 'example', - ], - 'pageCustomVariables:array' => [ - 'example', - ], - 'customCampaignNameQueryParam:string' => 'Example name', - 'customCampaignKeywordParam:string' => 'example_customCampaignKeywordParam', - 'doNotTrack:bool' => true, - 'disableCookies:bool' => true, - 'trackNoScript:bool' => true, - 'crossDomain:bool' => true, - 'forceMatomoEndpoint:bool' => true, - 'excludedQueryParams:string|string[]' => [ - 'example', - ], - 'excludedReferrers:string|string[]' => [ - 'example', - ], - 'disableCampaignParameters:bool' => true, - 'actionName:string|false' => 'Example name', - 'idGoal:int|false' => 1, - 'revenue:int|float|false' => 1.5, - 'group:string' => 'example_group', - 'fetchAliasUrls:bool' => true, - 'pattern:string|false' => 'example_pattern', - 'limit:int|false' => 100, - 'sitesToExclude:int[]' => [ - 'example', - ], - 'permission:string' => 'example_permission', - 'limit:int|null' => 100, - 'siteTypesToExclude:string[]' => [ - 'example', - ], - '_restrictSitesToLogin:string|false' => 'example__restrictSitesToLogin', - 'siteName:string' => 'Example name', - 'urls:string[]|string|null' => 'https://example.com/', - 'ecommerce:int|null' => 10, - 'siteSearch:int|null' => 10, - 'searchKeywordParameters:string|null' => 'example_searchKeywordParameters', - 'searchCategoryParameters:string|null' => 'example_searchCategoryParameters', - 'excludedIps:string|null' => 'example_excludedIps', - 'excludedQueryParameters:string|null' => 'example_excludedQueryParameters', - 'group:string|null' => 'example_group', - 'startDate:string|null' => '2026-05-01 00:00:00', - 'excludedUserAgents:string|null' => 'example_excludedUserAgents', - 'keepURLFragments:int|null' => 10, - 'type:string|null' => 'example_type', - 'settingValues:SettingValues|null' => 'example_settingValues', - 'excludeUnknownUrls:bool|null' => true, - 'excludedReferrers:string|null' => 'example_excludedReferrers', - 'passwordConfirmation:string|null' => 'example_passwordConfirmation', - 'urls:string[]|string' => 'https://example.com/', - 'ipRange:string' => 'example_ipRange', - 'excludedIps:string' => 'example_excludedIps', - 'searchKeywordParameters:string' => 'example_searchKeywordParameters', - 'searchCategoryParameters:string' => 'example_searchCategoryParameters', - 'excludedUserAgents:string' => 'example_excludedUserAgents', - 'excludedReferrers:string' => 'example_excludedReferrers', + 'definition:string' => 'countryCode==NZ', + 'permission:string' => 'view', + 'siteName:string' => 'Example NZ Site', + 'urls:string[]|string' => [ + 'https://example.org', + 'https://example.org/pricing', + ], + 'ipRange:string' => '203.0.113.0/24', + 'excludedIps:string' => '203.0.113.10,203.0.113.11', + 'searchKeywordParameters:string' => 'q,query,keyword', + 'searchCategoryParameters:string' => 'category,cat', + 'excludedUserAgents:string' => 'HeadlessChrome,Googlebot', + 'excludedReferrers:string' => 'partner.example.com,internal.example.org', 'enabled:bool' => true, - 'defaultCurrency:string' => 'example_defaultCurrency', - 'defaultTimezone:string' => 'example_defaultTimezone', - 'exclusionType:string' => 'example_exclusionType', - 'queryParamsToExclude:string|null' => 'example_queryParamsToExclude', - 'siteName:string|null' => 'Example name', - 'countryCode:string|null' => 'example_countryCode', - 'multipleTimezonesInCountry:bool|null' => true, - 'oldGroupName:string' => 'Example name', - 'newGroupName:string' => 'Example name', - 'idContext:string' => 'example_idContext', - 'idContainer:string' => 'example_idContainer', - 'environment:string' => 'example_environment', - 'jsFramework:string' => 'example_jsFramework', - 'idContainerVersion:int' => 1, - 'fireTriggerIds:int[]' => [ - 'example', - ], - 'blockTriggerIds:int[]' => [ - 'example', - ], - 'fireLimit:string' => 'example_fireLimit', - 'fireDelay:int' => 10, - 'priority:int' => 10, - 'endDate:string|null' => '2026-05-01 00:00:00', - 'description:string|null' => 'Example description', - 'status:string' => 'running', - 'idTag:int' => 1, - 'idTrigger:int' => 1, - 'conditions:array>' => [ - 'example', - ], - 'idVariable:int' => 1, - 'defaultValue:bool|float|int|string|null' => true, - 'lookupTable:array>' => [ - 'example', - ], - 'context:string' => 'example_context', - 'ignoreGtmDataLayer:int' => 10, - 'isTagFireLimitAllowedInPreviewMode:int' => 100, - 'activelySyncGtmDataLayer:int' => 10, - 'idContainerVersion:int|null' => 1, - 'exportedContainerVersion:string' => 'example_exportedContainerVersion', - 'backupName:string' => 'Example name', - '_isDraftRestoreCall:bool' => true, - 'id:string' => 'example_id', - 'pageTitle:string' => 'example_pageTitle', - 'limitBeforeGrouping:int|string' => 100, - 'actionName:string' => 'Example name', + 'defaultCurrency:string' => 'NZD', + 'defaultTimezone:string' => 'Pacific/Auckland', + 'exclusionType:string' => 'ip', + 'oldGroupName:string' => 'Marketing Team', + 'newGroupName:string' => 'Growth Team', + 'idContext:string' => 'web', + 'idContainer:string' => 'GTMNZ123', + 'environment:string' => 'live', + 'idContainerVersion:int' => 3, + 'idTag:int' => 2, + 'idTrigger:int' => 3, + 'idVariable:int' => 4, + 'context:string' => 'web', + 'exportedContainerVersion:string' => 'live-v3', + 'id:string' => 'pricing-signup-goal', + 'pageTitle:string' => 'Pricing', + 'actionName:string' => 'Pricing Page', 'actionType:\'url\'|\'title\'' => 'url', 'actionType:"url"|"title"' => 'url', - 'parts:string' => 'example_parts', - 'userLogin:string' => 'example_userLogin', - 'ip:string|false' => 'example_ip', - 'provider:string|false' => 'example_provider', - 'providerId:string' => 'example_providerId', - 'dataSource:string|false' => 'example_dataSource', - 'limitActionsPerStep:int' => 100, - 'exploreStep:int|false' => 10, - 'exploreUrl:string|false' => 'https://example.com/', - 'interactionPosition:string' => 'example_interactionPosition', - 'offsetActionsPerStep:int|false' => 0, - 'preferenceName:string' => 'Example name', - 'preferenceValue:mixed' => 'example_preferenceValue', - 'userLogin:string|null|false' => 'example_userLogin', - 'offset:int|null' => 0, - 'filter_search:string|null' => 'example_filter_search', - 'filter_access:string|null' => 'example_filter_access', - 'filter_status:string|null' => 'running', - 'userLogins:string' => 'example_userLogins', - 'access:string' => 'example_access', - 'userEmail:string' => 'user@example.com', - 'password:string' => 'example_password', - 'email:string' => 'user@example.com', - '_isPasswordHashed:bool' => true, - 'initialIdSite:int|null' => 10, - 'expiryInDays:int|null' => 10, + 'userLogin:string' => 'alice', + 'providerId:string' => 'google', + 'interactionPosition:string' => 'header', + 'preferenceName:string' => 'reportFormat', + 'preferenceValue:mixed' => 'pdf', + 'access:string' => 'view', + 'userEmail:string' => 'alice@example.org', + 'password:string' => 'correct-horse-battery-staple', + 'email:string' => 'alice@example.org', 'hasSuperUserAccess:bool|int|string' => true, - 'password:string|false' => 'example_password', - 'email:string|false' => 'user@example.com', - 'passwordConfirmation:string|false' => 'example_passwordConfirmation', - 'access:string|list' => 'example_access', - 'idSites:string|int|int[]' => [ - 1, - 2, - ], - 'capabilities:string|string[]' => [ - 'example', - ], - 'idSites:int|int[]|string' => 1, - 'expireDate:string|null' => '2026-05-01', - 'expireHours:int|string' => 10, - 'secureOnly:bool' => true, - 'expiryInDays:int' => 10, - 'columns:list|string|null' => 'example_columns', - 'hideFutureHoursWhenToday:bool' => true, -]; \ No newline at end of file + 'access:string|list' => ['view'], + 'idSites:string|int|int[]' => [1, 2], + 'capabilities:string|string[]' => ['read:reports'], + 'idSites:int|int[]|string' => [1, 2], + 'passwordConfirmation:string' => 'correct-horse-battery-staple', +]; From 834255b781fb60afea7c2c288ef154684b71f60e Mon Sep 17 00:00:00 2001 From: Lachlan Reynolds Date: Fri, 15 May 2026 11:27:28 +1200 Subject: [PATCH 3/5] phpcs --- config/ParameterExamples.php | 1 + tests/Resources/MockAnnotationGenerator.php | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/config/ParameterExamples.php b/config/ParameterExamples.php index 883b57d..fccea87 100644 --- a/config/ParameterExamples.php +++ b/config/ParameterExamples.php @@ -1,4 +1,5 @@ 1, 'period:\'day\'|\'week\'|\'month\'|\'year\'|\'range\'' => 'day', diff --git a/tests/Resources/MockAnnotationGenerator.php b/tests/Resources/MockAnnotationGenerator.php index 31b1bd9..23e4c6e 100644 --- a/tests/Resources/MockAnnotationGenerator.php +++ b/tests/Resources/MockAnnotationGenerator.php @@ -110,5 +110,4 @@ public function shouldUseParameterLevelExample(array $typesMap, string $example) { return parent::shouldUseParameterLevelExample($typesMap, $example); } - } From ab120ddd06d39eca109df312b26e455b51379174 Mon Sep 17 00:00:00 2001 From: Lachlan Reynolds Date: Fri, 15 May 2026 11:44:01 +1200 Subject: [PATCH 4/5] Fix tests --- config/ParameterExamples.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/ParameterExamples.php b/config/ParameterExamples.php index fccea87..5ba222c 100644 --- a/config/ParameterExamples.php +++ b/config/ParameterExamples.php @@ -72,8 +72,8 @@ 1, 2, ], - 'period:\'day\'|\'week\'|\'month\'' => 'week', - 'period:"day"|"week"|"month"' => 'week', + 'period:\'day\'|\'week\'|\'month\'' => 'day', + 'period:"day"|"week"|"month"' => 'day', 'emailMe:bool' => true, 'additionalEmails:list' => [ 'alice@example.org', From bca8e0e19ee297d35bc7d28a55033434b3d65da8 Mon Sep 17 00:00:00 2001 From: Lachlan Reynolds Date: Fri, 15 May 2026 13:44:03 +1200 Subject: [PATCH 5/5] Added missing required examples and fixed default logic --- Annotations/AnnotationGenerator.php | 4 +++- config/ParameterExamples.php | 17 ++++++++++++++--- tests/Unit/AnnotationGeneratorTest.php | 10 ++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Annotations/AnnotationGenerator.php b/Annotations/AnnotationGenerator.php index 2dbc9d8..b3103f8 100644 --- a/Annotations/AnnotationGenerator.php +++ b/Annotations/AnnotationGenerator.php @@ -520,7 +520,9 @@ public function buildParameterAnnotationData(string $methodName, string $paramNa $description = $this->normaliseDescriptionText($description); $default = $paramMetadata['default'] ?? null; - if (!is_string($default)) { + if ($default === null) { + $default = NoDefaultValue::class; + } elseif (!is_string($default)) { $default = json_encode($default); } diff --git a/config/ParameterExamples.php b/config/ParameterExamples.php index 5ba222c..2a4452f 100644 --- a/config/ParameterExamples.php +++ b/config/ParameterExamples.php @@ -4,7 +4,7 @@ 'idSite:int|string|int[]' => 1, 'period:\'day\'|\'week\'|\'month\'|\'year\'|\'range\'' => 'day', 'period:"day"|"week"|"month"|"year"|"range"' => 'day', - 'date:string' => '2026-05-01', + 'date:string' => 'yesterday', 'idSite:int|string' => 1, 'apiModule:string' => 'VisitsSummary', 'apiAction:string' => 'get', @@ -146,9 +146,12 @@ 'verificationCode:string' => '123456', 'delegatedManagement:bool' => true, 'isEnabled:bool|int|string' => true, - 'clientId:string' => 'analytics-dashboard', + 'memberOf:string' => 'cn=matomo-users,ou=groups,dc=example,dc=org', + 'filter:string' => '(objectClass=person)', + 'initialIdSite:int' => 1, + 'clientId:string' => '0123456789abcdef0123456789abcdef', 'grantTypes:string[]' => ['authorization_code'], - 'scope:string' => 'read:reports', + 'scope:string' => 'matomo:read', 'active:string' => '1', 'plugin:string' => 'HeatmapSessionRecording', 'format:string' => 'html', @@ -167,6 +170,14 @@ 1, 2, ], + 'sourceIdSites:integer|(integer|array)' => [ + 1, + 2, + ], + 'sourceIdSites:integer | (integer | array)' => [ + 1, + 2, + ], 'timezone:string' => 'Pacific/Auckland', 'currency:string' => 'NZD', 'period:\'day\'|\'week\'|\'month\'|\'never\'' => 'never', diff --git a/tests/Unit/AnnotationGeneratorTest.php b/tests/Unit/AnnotationGeneratorTest.php index 9e3e7da..77c841a 100644 --- a/tests/Unit/AnnotationGeneratorTest.php +++ b/tests/Unit/AnnotationGeneratorTest.php @@ -544,6 +544,16 @@ public function getTestDataForBuildParameterAnnotationData(): iterable 'default' => 'false', 'example' => '', ]]; + yield 'should not include null as a default value' => ['someParam', [ + 'default' => null, + ], [], [ + 'name' => 'someParam', + 'types' => ['string' => null], + 'description' => '', + 'required' => 'false', + 'default' => 'Piwik\API\NoDefaultValue', + 'example' => '', + ]]; yield 'should still count empty string as a default value' => ['someParam', [ 'default' => '', ], [], [