PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.0.7
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.0.7
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / API / DocumentationGenerator.php
matomo / app / core / API Last commit date
DataTableManipulator 2 years ago ApiRenderer.php 2 years ago CORSHandler.php 2 years ago DataTableGenericFilter.php 2 years ago DataTableManipulator.php 2 years ago DataTablePostProcessor.php 2 years ago DocumentationGenerator.php 2 years ago Inconsistencies.php 2 years ago NoDefaultValue.php 2 years ago Proxy.php 2 years ago Request.php 2 years ago ResponseBuilder.php 2 years ago
DocumentationGenerator.php
352 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 *
9 */
10 namespace Piwik\API;
11
12 use Exception;
13 use Piwik\Common;
14 use Piwik\Container\StaticContainer;
15 use Piwik\Piwik;
16 use Piwik\Url;
17 use ReflectionClass;
18 /**
19 * Possible tags to use in APIs
20 *
21 * @hide -> Won't be shown in list of all APIs but is also not possible to be called via HTTP API
22 * @hideForAll Same as @hide
23 * @hideExceptForSuperUser Same as @hide but still shown and possible to be called by a user with super user access
24 * @internal -> Won't be shown in list of all APIs but is possible to be called via HTTP API
25 */
26 class DocumentationGenerator
27 {
28 protected $countPluginsLoaded = 0;
29 /**
30 * trigger loading all plugins with an API.php file in the Proxy
31 */
32 public function __construct()
33 {
34 $plugins = \Piwik\Plugin\Manager::getInstance()->getLoadedPluginsName();
35 foreach ($plugins as $plugin) {
36 try {
37 $className = \Piwik\API\Request::getClassNameAPI($plugin);
38 \Piwik\API\Proxy::getInstance()->registerClass($className);
39 } catch (Exception $e) {
40 }
41 }
42 }
43 /**
44 * Returns a HTML page containing help for all the successfully loaded APIs.
45 *
46 * @param bool $outputExampleUrls
47 * @return string
48 */
49 public function getApiDocumentationAsString($outputExampleUrls = true)
50 {
51 list($toc, $str) = $this->generateDocumentation($outputExampleUrls, $prefixUrls = '', $displayTitlesAsEnrichedHeadline = true);
52 return "<div vue-entry=\"CoreHome.ContentBlock\" content-title='Quick access to APIs' id='topApiRef' name='topApiRef'>\n\t\t\t\t{$toc}</div>\n\t\t\t\t{$str}";
53 }
54 /**
55 * Used on developer.piwik.org
56 *
57 * @param bool|true $outputExampleUrls
58 * @param string $prefixUrls
59 * @return string
60 */
61 public function getApiDocumentationAsStringForDeveloperReference($outputExampleUrls = true, $prefixUrls = '')
62 {
63 list($toc, $str) = $this->generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline = false);
64 return "<h2 id='topApiRef' name='topApiRef'>Quick access to APIs</h2>\n\t\t\t\t{$toc}\n\t\t\t\t{$str}";
65 }
66 protected function prepareModuleToDisplay($moduleName)
67 {
68 return "<a href='#{$moduleName}'>{$moduleName}</a><br/>";
69 }
70 protected function prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline)
71 {
72 $str = '';
73 $str .= "\n<a name='{$moduleName}' id='{$moduleName}'></a>";
74 if ($displayTitlesAsEnrichedHeadline) {
75 $str .= "<div vue-entry=\"CoreHome.ContentBlock\" content-title='Module " . $moduleName . "'>";
76 } else {
77 $str .= "<h2>Module " . $moduleName . "</h2>";
78 }
79 $info['__documentation'] = $this->checkDocumentation($info['__documentation']);
80 $str .= "<div class='apiDescription'> " . $info['__documentation'] . " </div>";
81 foreach ($methods as $methodName) {
82 if (\Piwik\API\Proxy::getInstance()->isDeprecatedMethod($class, $methodName)) {
83 continue;
84 }
85 $params = $this->getParametersString($class, $methodName);
86 $str .= "\n <div class='apiMethod'>- <b>{$moduleName}.{$methodName} </b>" . $params . "";
87 $str .= '<small>';
88 if ($outputExampleUrls) {
89 $str .= $this->addExamples($class, $methodName, $prefixUrls);
90 }
91 $str .= '</small>';
92 $str .= "</div>\n";
93 }
94 if ($displayTitlesAsEnrichedHeadline) {
95 $str .= "</div>";
96 }
97 return $str;
98 }
99 protected function prepareModulesAndMethods($info, $moduleName)
100 {
101 $toDisplay = array();
102 foreach ($info as $methodName => $infoMethod) {
103 if ($methodName == '__documentation') {
104 continue;
105 }
106 $toDisplay[$moduleName][] = $methodName;
107 }
108 return $toDisplay;
109 }
110 protected function addExamples($class, $methodName, $prefixUrls)
111 {
112 $token = Piwik::getCurrentUserTokenAuth();
113 $token_auth_url = "&token_auth=" . $token;
114 if ($token !== 'anonymous') {
115 $token_auth_url .= "&force_api_session=1";
116 }
117 $parametersToSet = array('idSite' => Common::getRequestVar('idSite', 1, 'int'), 'period' => Common::getRequestVar('period', 'day', 'string'), 'date' => Common::getRequestVar('date', 'today', 'string'));
118 $str = '';
119 $str .= "<span class=\"example\">";
120 $exampleUrl = $this->getExampleUrl($class, $methodName, $parametersToSet);
121 if ($exampleUrl !== false) {
122 $lastNUrls = '';
123 if (preg_match('/(&period)|(&date)/', $exampleUrl)) {
124 $exampleUrlRss = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet);
125 $lastNUrls = ", RSS of the last <a target='_blank' href='{$exampleUrlRss}&format=rss{$token_auth_url}&translateColumnNames=1'>10 days</a>";
126 }
127 $exampleUrl = $prefixUrls . $exampleUrl;
128 $str .= " [ Example in\n <a target='_blank' href='{$exampleUrl}&format=xml{$token_auth_url}'>XML</a>,\n <a target='_blank' href='{$exampleUrl}&format=JSON{$token_auth_url}'>Json</a>,\n <a target='_blank' href='{$exampleUrl}&format=Tsv{$token_auth_url}&translateColumnNames=1'>Tsv (Excel)</a>\n {$lastNUrls}\n ]";
129 } else {
130 $str .= " [ No example available ]";
131 }
132 $str .= "</span>";
133 return $str;
134 }
135 /**
136 * Check if Class contains @hide
137 *
138 * @param ReflectionClass $rClass instance of ReflectionMethod
139 * @return bool
140 */
141 public function checkIfClassCommentContainsHideAnnotation(ReflectionClass $rClass)
142 {
143 return false !== strstr($rClass->getDocComment(), '@hide');
144 }
145 /**
146 * Check if Class contains @internal
147 *
148 * @param ReflectionClass|\ReflectionMethod $rClass instance of ReflectionMethod
149 * @return bool
150 */
151 private function checkIfCommentContainsInternalAnnotation($rClass)
152 {
153 return false !== strstr($rClass->getDocComment(), '@internal');
154 }
155 /**
156 * Check if documentation contains @hide annotation and deletes it
157 *
158 * @param $moduleToCheck
159 * @return mixed
160 */
161 public function checkDocumentation($moduleToCheck)
162 {
163 if (strpos($moduleToCheck, '@hide') == true) {
164 $moduleToCheck = str_replace(strtok(strstr($moduleToCheck, '@hide'), "\n"), "", $moduleToCheck);
165 }
166 return $moduleToCheck;
167 }
168 /**
169 * Returns a string containing links to examples on how to call a given method on a given API
170 * It will export links to XML, CSV, HTML, JSON, PHP, etc.
171 * It will not export links for methods such as deleteSite or deleteUser
172 *
173 * @param string $class the class
174 * @param string $methodName the method
175 * @param array $parametersToSet parameters to set
176 * @return string|bool when not possible
177 */
178 public function getExampleUrl($class, $methodName, $parametersToSet = array())
179 {
180 $knowExampleDefaultParametersValues = array('access' => 'view', 'userLogin' => 'test', 'passwordMd5ied' => 'passwordExample', 'email' => 'test@example.org', 'languageCode' => 'fr', 'url' => 'https://divezone.net/', 'pageUrl' => 'https://divezone.net/', 'apiModule' => 'UserCountry', 'apiAction' => 'getCountry', 'lastMinutes' => '30', 'abandonedCarts' => '0', 'segmentName' => 'pageTitle', 'ip' => '194.57.91.215', 'idSites' => '1,2', 'idAlert' => '1', 'seconds' => '3600');
181 foreach ($parametersToSet as $name => $value) {
182 $knowExampleDefaultParametersValues[$name] = $value;
183 }
184 // no links for these method names
185 $doNotPrintExampleForTheseMethods = array(
186 //Sites
187 'deleteSite',
188 'addSite',
189 'updateSite',
190 'addSiteAliasUrls',
191 //Users
192 'deleteUser',
193 'addUser',
194 'updateUser',
195 'setUserAccess',
196 //Goals
197 'addGoal',
198 'updateGoal',
199 'deleteGoal',
200 //Marketplace
201 'deleteLicenseKey',
202 );
203 if (in_array($methodName, $doNotPrintExampleForTheseMethods)) {
204 return false;
205 }
206 // we try to give an URL example to call the API
207 $aParameters = \Piwik\API\Proxy::getInstance()->getParametersList($class, $methodName);
208 $aParameters['format'] = false;
209 $aParameters['hideIdSubDatable'] = false;
210 $aParameters['serialize'] = false;
211 $aParameters['language'] = false;
212 $aParameters['translateColumnNames'] = false;
213 $aParameters['label'] = false;
214 $aParameters['labelSeries'] = false;
215 $aParameters['flat'] = false;
216 $aParameters['include_aggregate_rows'] = false;
217 $aParameters['filter_offset'] = false;
218 $aParameters['filter_limit'] = false;
219 $aParameters['filter_sort_column'] = false;
220 $aParameters['filter_sort_order'] = false;
221 $aParameters['filter_excludelowpop'] = false;
222 $aParameters['filter_excludelowpop_value'] = false;
223 $aParameters['filter_column_recursive'] = false;
224 $aParameters['filter_pattern'] = false;
225 $aParameters['filter_pattern_recursive'] = false;
226 $aParameters['filter_truncate'] = false;
227 $aParameters['hideColumns'] = false;
228 $aParameters['hideColumnsRecursively'] = false;
229 $aParameters['showColumns'] = false;
230 $aParameters['pivotBy'] = false;
231 $aParameters['pivotByColumn'] = false;
232 $aParameters['pivotByColumnLimit'] = false;
233 $aParameters['disable_queued_filters'] = false;
234 $aParameters['disable_generic_filters'] = false;
235 $aParameters['expanded'] = false;
236 $aParameters['idDimenson'] = false;
237 $aParameters['format_metrics'] = false;
238 $aParameters['compare'] = false;
239 $aParameters['compareDates'] = false;
240 $aParameters['comparePeriods'] = false;
241 $aParameters['compareSegments'] = false;
242 $aParameters['comparisonIdSubtables'] = false;
243 $aParameters['invert_compare_change_compute'] = false;
244 $aParameters['filter_update_columns_when_show_all_goals'] = false;
245 $aParameters['filter_show_goal_columns_process_goals'] = false;
246 $extraParameters = StaticContainer::get('entities.idNames');
247 $extraParameters = array_merge($extraParameters, StaticContainer::get('DocumentationGenerator.customParameters'));
248 foreach ($extraParameters as $paramName) {
249 if (isset($aParameters[$paramName])) {
250 continue;
251 }
252 $aParameters[$paramName] = false;
253 }
254 $moduleName = \Piwik\API\Proxy::getInstance()->getModuleNameFromClassName($class);
255 $aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters);
256 foreach ($aParameters as $nameVariable => &$defaultValue) {
257 if (isset($knowExampleDefaultParametersValues[$nameVariable])) {
258 $defaultValue = $knowExampleDefaultParametersValues[$nameVariable];
259 } elseif ($defaultValue instanceof \Piwik\API\NoDefaultValue) {
260 // if there isn't a default value for a given parameter,
261 // we need a 'know default value' or we can't generate the link
262 return false;
263 }
264 }
265 return '?' . Url::getQueryStringFromParameters($aParameters);
266 }
267 /**
268 * Returns the methods $class.$name parameters (and default value if provided) as a string.
269 *
270 * @param string $class The class name
271 * @param string $name The method name
272 * @return string For example "(idSite, period, date = 'today')"
273 */
274 protected function getParametersString($class, $name)
275 {
276 $aParameters = \Piwik\API\Proxy::getInstance()->getParametersListWithTypes($class, $name);
277 $asParameters = array();
278 foreach ($aParameters as $nameVariable => $parameter) {
279 // Do not show API parameters starting with _
280 // They are supposed to be used only in internal API calls
281 if (strpos($nameVariable, '_') === 0) {
282 continue;
283 }
284 $str = '';
285 if (!empty($parameter['type'])) {
286 $prefix = $parameter['allowsNull'] ? '?' : '';
287 $str = '<i>' . $prefix . $parameter['type'] . '</i> ';
288 }
289 $str .= $nameVariable;
290 $defaultValue = $parameter['default'];
291 if (!$defaultValue instanceof \Piwik\API\NoDefaultValue) {
292 if (is_array($defaultValue)) {
293 $str .= " = 'Array'";
294 } elseif (!empty($parameter['type']) && $parameter['allowsNull']) {
295 $str .= "";
296 // don't display default value, as the ? before the type hint indicates it's optional
297 } elseif ($parameter['type'] === 'bool' && $defaultValue === true) {
298 $str .= " = true";
299 } elseif ($parameter['type'] === 'bool' && $defaultValue === false) {
300 $str .= " = false";
301 } else {
302 $str .= " = '{$defaultValue}'";
303 }
304 }
305 $asParameters[] = $str;
306 }
307 $sParameters = implode(", ", $asParameters);
308 return "({$sParameters})";
309 }
310 /**
311 * @param $outputExampleUrls
312 * @param $prefixUrls
313 * @param $displayTitlesAsEnrichedHeadline
314 * @return array
315 */
316 protected function generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline)
317 {
318 $str = $toc = '';
319 foreach (\Piwik\API\Proxy::getInstance()->getMetadata() as $class => $info) {
320 $moduleName = \Piwik\API\Proxy::getInstance()->getModuleNameFromClassName($class);
321 $rClass = new ReflectionClass($class);
322 if (!Piwik::hasUserSuperUserAccess() && $this->checkIfClassCommentContainsHideAnnotation($rClass)) {
323 continue;
324 }
325 if ($this->checkIfCommentContainsInternalAnnotation($rClass)) {
326 continue;
327 }
328 $toDisplay = $this->prepareModulesAndMethods($info, $moduleName);
329 foreach ($toDisplay as $moduleName => $methods) {
330 foreach ($methods as $index => $method) {
331 if (!method_exists($class, $method)) {
332 // method is handled through API.Request.intercept event
333 continue;
334 }
335 $reflectionMethod = new \ReflectionMethod($class, $method);
336 if ($this->checkIfCommentContainsInternalAnnotation($reflectionMethod)) {
337 unset($toDisplay[$moduleName][$index]);
338 }
339 }
340 if (empty($toDisplay[$moduleName])) {
341 unset($toDisplay[$moduleName]);
342 }
343 }
344 foreach ($toDisplay as $moduleName => $methods) {
345 $toc .= $this->prepareModuleToDisplay($moduleName);
346 $str .= $this->prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline);
347 }
348 }
349 return array($toc, $str);
350 }
351 }
352