Skip to content

Commit cb8d3e1

Browse files
authored
Merge pull request #22 from OSInet/21-undeclared_improvements
Undeclared improvements
2 parents db76fcd + 0d74d5d commit cb8d3e1

File tree

12 files changed

+129
-64
lines changed

12 files changed

+129
-64
lines changed

qa.module

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,54 @@
88
*
99
* @since DRUPAL-4-6
1010
*
11-
* @license Licensed under the disjunction of the CeCILL, version 2 and General
12-
* Public License version 2 and later
11+
* @license GPL-2.0-or-later
1312
*
14-
* License note: QA is distributed by OSInet to its customers under the
15-
* CeCILL 2.0 license. OSInet support services only apply to the module
16-
* when distributed by OSInet, not by any third-party further down the
17-
* distribution chain.
13+
* License note: OSInet support services only apply to the module when
14+
* distributed by OSInet, not by any third-party further down the distribution
15+
* chain enabled by GPL.
1816
*
19-
* If you obtained QA from drupal.org, that site received it under the
20-
* GPLv2 license and can therefore distribute it under the GPLv2, and
21-
* so can you and just anyone down the chain as long as the GPLv2 terms
22-
* are abided by, the module distributor in that case being the
23-
* drupal.org organization or the downstream distributor, not OSInet.
17+
* If you obtained QA from another source, that source received it under the
18+
* GPL, and can therefore distribute it under the GPL, and so can you and just
19+
* anyone down the chain as long as the GPL terms are abided by, the module
20+
* distributor in that case being that source, not OSInet.
2421
*/
2522

2623
use Drupal\Component\Utility\Xss;
24+
use Drupal\Core\Form\FormStateInterface;
2725
use Drupal\Core\Url;
2826
use Drupal\qa\BaseControl;
2927
use Drupal\qa\Exportable;
30-
use Drupal\qa\Variable\Variable;
28+
use Drupal\qa\Variable;
3129
use Symfony\Component\HttpFoundation\RedirectResponse;
3230

3331
/**
34-
* Page callback for qa/dependencies
32+
* Page callback for qa/dependencies.
3533
*
36-
* TODO convert to native Image_GraphViz to remove dependency on graphviz_filter
37-
* XXX convert to Grafizzi to remove dependency on Image_GraphViz
34+
* TODO convert to native Image_GraphViz to stop depending on graphviz_filter.
35+
* XXX convert to Grafizzi to remove dependency on Image_GraphViz.
3836
*
3937
* @return string
38+
* A DOT digraph file.
4039
*/
4140
function qa_page_dependencies() {
4241
/** @var \Drupal\qa\Dependencies $qaDep */
4342
$qaDep = Drupal::service('qa.dependencies');
44-
$G = $qaDep->build();
45-
// passed by reference: cannot pass a function return
46-
return graphviz_filter_render($G);
43+
$graph = $qaDep->build();
44+
// Passed by reference: cannot pass a function return.
45+
return graphviz_filter_render($graph);
4746
}
4847

4948
/**
5049
* Batch conclusion callback.
5150
*
52-
* @param boolean $success
51+
* @param bool $success
52+
* Did the batch succeed ?
5353
* @param array $results
54+
* The batch steps results.
5455
* @param array $operations
56+
* The operations performed during the batch.
5557
*/
56-
function qa_report_finished($success, $results, $operations) {
58+
function qa_report_finished(bool $success, array $results, array $operations) {
5759
unset($results['#message']);
5860
if ($success) {
5961
$message = Drupal::translation()
@@ -77,7 +79,7 @@ function qa_report_results() {
7779
if (empty($_SESSION['qa_results'])) {
7880
return new RedirectResponse(Url::fromRoute('qa.reports'));
7981
}
80-
// Work around incomplete classes
82+
// Work around incomplete classes.
8183
$results = unserialize(serialize($_SESSION['qa_results']));
8284

8385
$header = [
@@ -92,12 +94,12 @@ function qa_report_results() {
9294
$data[] = [
9395
$control->title,
9496
$pass->status
95-
? $r->render([
97+
? $r->render([
9698
'#theme' => 'image',
9799
'#path' => 'misc/watchdog-ok.png',
98100
'#alt' => t('OK'),
99101
])
100-
: $r->render([
102+
: $r->render([
101103
'#theme' => 'image',
102104
'#path' => 'misc/watchdog-error.png',
103105
'#alt' => t('Error'),
@@ -114,14 +116,15 @@ function qa_report_results() {
114116
],
115117
'#attached' => ['library' => ['qa/results']],
116118
]);
117-
// unset($_SESSION['qa_results']);
119+
// unset($_SESSION['qa_results']).
118120
return $ret;
119121
}
120122

121123
/**
122124
* Form builder for QA packages/controls selection form.
123125
*
124126
* @return array
127+
* A form array.
125128
*/
126129
function qa_report_form($form, $form_state) {
127130
$form = [];
@@ -186,12 +189,14 @@ function qa_report_form($form, $form_state) {
186189
}
187190

188191
/**
189-
* Submit handler for QA packages/controls selection form
192+
* Submit handler for QA packages/controls selection form.
190193
*
191194
* @param array $form
192-
* @param array $form_state
195+
* The form array.
196+
* @param \Drupal\Core\Form\FormStateInterface $form_state
197+
* The form state.
193198
*/
194-
function qa_report_form_submit($form, &$form_state) {
199+
function qa_report_form_submit(array $form, FormStateInterface $form_state) {
195200
$controls = [];
196201
foreach ($form_state['values'] as $item => $value) {
197202
if (class_exists($item) && is_subclass_of($item,
@@ -218,10 +223,10 @@ function qa_report_form_submit($form, &$form_state) {
218223
'operations' => [],
219224
'title' => t('QA Controls running'),
220225
'init_message' => t('QA Controls initializing'),
221-
// 'progress_message' => t('current: @current, Remaining: @remaining, Total: @total'),
226+
'progress_message' => t('current: @current, Remaining: @remaining, Total: @total'),
222227
'error_message' => t('Error in QA Control'),
223228
'finished' => 'qa_report_finished',
224-
// 'file' => '', // only if outside module file
229+
// 'file' => '', // only if outside module file.
225230
];
226231

227232
foreach ($controls as $item => $value) {
@@ -232,10 +237,8 @@ function qa_report_form_submit($form, &$form_state) {
232237

233238
/**
234239
* Batch progress step.
235-
*
236-
* @return void
237240
*/
238-
function qa_report_run_pass($class_name, &$context) {
241+
function qa_report_run_pass($class_name, &$context): void {
239242
$name_arg = ['@class' => $class_name];
240243

241244
$control = new $class_name();
@@ -263,6 +266,15 @@ function qa_report_run_pass($class_name, &$context) {
263266
}
264267
}
265268

269+
/**
270+
* Load variable by name.
271+
*
272+
* @param string $name
273+
* The name of the variable.
274+
*
275+
* @return \Drupal\qa\Variable|false
276+
* The variable if it was found.
277+
*/
266278
function qa_variable_load($name) {
267279
$variable = new Variable($name);
268280
if (!$variable->is_set) {

qa.services.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ services:
33
class: Drupal\qa\Plugin\QaCheckManager
44
arguments:
55
- '@kernel'
6+
- '@extension.list.module'
67
parent: default_plugin_manager
78

89
logger.channel.qa:

src/Annotation/QaCheck.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types=1);
3+
declare(strict_types = 1);
44

55
namespace Drupal\qa\Annotation;
66

src/Pass.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types=1);
3+
declare(strict_types = 1);
44

55
namespace Drupal\qa;
66

src/Plugin/QaCheck/Dependencies/FunctionCallVisitor.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types=1);
3+
declare(strict_types = 1);
44

55
namespace Drupal\qa\Plugin\QaCheck\Dependencies;
66

@@ -33,9 +33,16 @@ public function __construct() {
3333
* {@inheritdoc}
3434
*/
3535
public function enterNode(Node $node) {
36-
// Method calls need a different handling, using MethodCall.
37-
if ($node instanceof FuncCall) {
38-
$this->pad[] = implode('\\', $node->name->parts);
36+
// Why this test ?
37+
// - Method calls need a different handling, using MethodCall.
38+
// - Closure calls are named by the variable holding them, and are
39+
// necessarily defined, so don't need to be tracked.
40+
if ($node instanceof FuncCall && isset($node->name->parts)) {
41+
$func = implode('\\', $node->name->parts);
42+
if (!isset($this->pad[$func])) {
43+
$this->pad[$func] = [];
44+
}
45+
$this->pad[$func][] = $node->getAttribute('startLine') ?? '?';
3946
}
4047
}
4148

src/Plugin/QaCheck/Dependencies/Undeclared.php

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types=1);
3+
declare(strict_types = 1);
44

55
namespace Drupal\qa\Plugin\QaCheck\Dependencies;
66

@@ -149,11 +149,11 @@ protected function functionCalls(string $path): array {
149149
$traverser = new NodeTraverser();
150150
$traverser->addVisitor($visitor = new FunctionCallVisitor());
151151
$traverser->traverse($stmts);
152-
$pad = array_unique($visitor->pad);
153152
// Ignore builtin/extension functions.
154-
$pad = array_filter($pad, function ($name) {
155-
return empty($this->qam->internalFunctions[$name]);
156-
});
153+
$pad = array_filter($visitor->pad, function ($name) {
154+
$isInternal = isset($this->qam->internalFunctions[$name]);
155+
return !$isInternal;
156+
}, ARRAY_FILTER_USE_KEY);
157157
return $pad;
158158
}
159159

@@ -170,19 +170,22 @@ protected function functionCalls(string $path): array {
170170
*/
171171
protected function moduleActualDependencies(string $name, array $calls): array {
172172
$modules = [];
173-
foreach ($calls as $called) {
173+
foreach ($calls as $called => $lines) {
174174
try {
175175
$rf = new \ReflectionFunction($called);
176176
}
177177
catch (\ReflectionException $e) {
178-
$modules[] = "(${called})";
178+
$modules["(${called})"][$called] = $lines;
179179
continue;
180180
}
181181

182182
// Drupal name-based magic.
183183
$module = basename($rf->getFileName(), '.module');
184184
if ($module !== $name) {
185-
$modules[] = $module;
185+
if (!isset($modules[$module][$called])) {
186+
$modules[$module][$called] = [];
187+
}
188+
$modules[$module][$called] += $lines;
186189
}
187190
}
188191
return $modules;
@@ -198,14 +201,10 @@ protected function moduleActualDependencies(string $name, array $calls): array {
198201
* A map of modules names by module name.
199202
*/
200203
protected function moduleDeclaredDependencies(string $name): array {
201-
$info = $this->elm->getExtensionInfo($name);
202-
$deps = $info['dependencies'] ?? [];
203-
$res = [];
204-
foreach ($deps as $dep) {
205-
$ar = explode(":", $dep);
206-
$res[] = array_pop($ar);
207-
}
208-
return $res;
204+
$ext = $this->elm->get($name);
205+
// XXX Undocumented API field.
206+
$deps = array_keys($ext->requires);
207+
return $deps;
209208
}
210209

211210
/**
@@ -226,7 +225,7 @@ public function check(): Result {
226225
$calls = $this->functionCalls($path);
227226
$actual = $this->moduleActualDependencies($module, $calls);
228227
$declared = $this->moduleDeclaredDependencies($module);
229-
$missing = array_diff($actual, $declared);
228+
$missing = array_diff_key($actual, array_flip($declared));
230229
if (!empty($missing)) {
231230
$undeclared[$module] = $missing;
232231
}

src/Plugin/QaCheck/References/Integrity.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types=1);
3+
declare(strict_types = 1);
44

55
namespace Drupal\qa\Plugin\QaCheck\References;
66

src/Plugin/QaCheck/References/TaxonomyIndex.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types=1);
3+
declare(strict_types = 1);
44

55
namespace Drupal\qa\Plugin\QaCheck\References;
66

src/Plugin/QaCheck/System/ExternalCode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
declare(strict_types=1);
3+
declare(strict_types = 1);
44

55
namespace Drupal\qa\Plugin\QaCheck\System;
66

src/Plugin/QaCheckManager.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use Drupal\Component\Discovery\DiscoveryException;
66
use Drupal\Core\DrupalKernelInterface;
7+
use Drupal\Core\Extension\Extension;
8+
use Drupal\Core\Extension\ModuleExtensionList;
79
use Drupal\Core\Plugin\DefaultPluginManager;
810
use Drupal\Core\Cache\CacheBackendInterface;
911
use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -39,6 +41,13 @@ class QaCheckManager extends DefaultPluginManager {
3941
*/
4042
protected $vendor;
4143

44+
/**
45+
* The extension.list.module service.
46+
*
47+
* @var \Drupal\Core\Extension\ModuleExtensionList
48+
*/
49+
protected $elm;
50+
4251
/**
4352
* Constructs a new QaCheckManager object.
4453
*
@@ -51,15 +60,19 @@ class QaCheckManager extends DefaultPluginManager {
5160
* The module handler to invoke the alter hook with.
5261
* @param \Drupal\Core\DrupalKernelInterface $kernel
5362
* The kernel service.
63+
* @param \Drupal\Core\Extension\ModuleExtensionList $elm
64+
* The extension.list.module service.
5465
*/
5566
public function __construct(
5667
Traversable $namespaces,
5768
CacheBackendInterface $cache_backend,
5869
ModuleHandlerInterface $module_handler,
59-
DrupalKernelInterface $kernel
70+
DrupalKernelInterface $kernel,
71+
ModuleExtensionList $elm
6072
) {
6173
parent::__construct('Plugin/QaCheck', $namespaces, $module_handler,
6274
QaCheckInterface::class, QaCheck::class);
75+
$this->elm = $elm;
6376
$this->root = realpath($kernel->getAppRoot());
6477
$this->vendor = self::getVendorDir();
6578

@@ -176,6 +189,16 @@ public function isInternal($file) {
176189
$this->vendor,
177190
];
178191

192+
$list = $this->elm->getList();
193+
$required = [];
194+
array_walk($list, function (Extension $ext) use (&$required) {
195+
// XXX Undocumented API field.
196+
if (!empty($ext->info['required'])) {
197+
$required[] = $this->root . '/' . $ext->getPath();
198+
}
199+
});
200+
$internal = array_merge($internal, $required);
201+
179202
foreach ($internal as $root) {
180203
if (strpos($file, $root) !== FALSE) {
181204
return TRUE;

0 commit comments

Comments
 (0)