PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.0.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.0.1
5.13.0 5.12.1 5.12.0 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
350 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 return false;
261 }
262 }
263 return '?' . Url::getQueryStringFromParameters($aParameters);
264 }
265 /**
266 * Returns the methods $class.$name parameters (and default value if provided) as a string.
267 *
268 * @param string $class The class name
269 * @param string $name The method name
270 * @return string For example "(idSite, period, date = 'today')"
271 */
272 protected function getParametersString($class, $name)
273 {
274 $aParameters = \Piwik\API\Proxy::getInstance()->getParametersListWithTypes($class, $name);
275 $asParameters = array();
276 foreach ($aParameters as $nameVariable => $parameter) {
277 // Do not show API parameters starting with _
278 // They are supposed to be used only in internal API calls
279 if (strpos($nameVariable, '_') === 0) {
280 continue;
281 }
282 $str = '';
283 if (!empty($parameter['type'])) {
284 $prefix = $parameter['allowsNull'] ? '?' : '';
285 $str = '<i>' . $prefix . $parameter['type'] . '</i> ';
286 }
287 $str .= $nameVariable;
288 $defaultValue = $parameter['default'];
289 if (!$defaultValue instanceof \Piwik\API\NoDefaultValue) {
290 if (is_array($defaultValue)) {
291 $str .= " = 'Array'";
292 } elseif (!empty($parameter['type']) && $parameter['allowsNull']) {
293 $str .= "";
294 // don't display default value, as the ? before the type hint indicates it's optional
295 } elseif ($parameter['type'] === 'bool' && $defaultValue === true) {
296 $str .= " = true";
297 } elseif ($parameter['type'] === 'bool' && $defaultValue === false) {
298 $str .= " = false";
299 } else {
300 $str .= " = '{$defaultValue}'";
301 }
302 }
303 $asParameters[] = $str;
304 }
305 $sParameters = implode(", ", $asParameters);
306 return "({$sParameters})";
307 }
308 /**
309 * @param $outputExampleUrls
310 * @param $prefixUrls
311 * @param $displayTitlesAsEnrichedHeadline
312 * @return array
313 */
314 protected function generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline)
315 {
316 $str = $toc = '';
317 foreach (\Piwik\API\Proxy::getInstance()->getMetadata() as $class => $info) {
318 $moduleName = \Piwik\API\Proxy::getInstance()->getModuleNameFromClassName($class);
319 $rClass = new ReflectionClass($class);
320 if (!Piwik::hasUserSuperUserAccess() && $this->checkIfClassCommentContainsHideAnnotation($rClass)) {
321 continue;
322 }
323 if ($this->checkIfCommentContainsInternalAnnotation($rClass)) {
324 continue;
325 }
326 $toDisplay = $this->prepareModulesAndMethods($info, $moduleName);
327 foreach ($toDisplay as $moduleName => $methods) {
328 foreach ($methods as $index => $method) {
329 if (!method_exists($class, $method)) {
330 // method is handled through API.Request.intercept event
331 continue;
332 }
333 $reflectionMethod = new \ReflectionMethod($class, $method);
334 if ($this->checkIfCommentContainsInternalAnnotation($reflectionMethod)) {
335 unset($toDisplay[$moduleName][$index]);
336 }
337 }
338 if (empty($toDisplay[$moduleName])) {
339 unset($toDisplay[$moduleName]);
340 }
341 }
342 foreach ($toDisplay as $moduleName => $methods) {
343 $toc .= $this->prepareModuleToDisplay($moduleName);
344 $str .= $this->prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline);
345 }
346 }
347 return array($toc, $str);
348 }
349 }
350