Skip to content

Commit 312641d

Browse files
Try to make PHPUnit 8.5 work with PHP 8 and Xdebug 3
1 parent 14eb51b commit 312641d

File tree

12 files changed

+194
-152
lines changed

12 files changed

+194
-152
lines changed

.psalm/baseline.xml

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -733,8 +733,9 @@
733733
</PossiblyInvalidPropertyAssignmentValue>
734734
</file>
735735
<file src="src/Framework/MockObject/MockMethod.php">
736-
<MissingThrowsDocblock occurrences="2">
736+
<MissingThrowsDocblock occurrences="3">
737737
<code>new \Text_Template($filename)</code>
738+
<code>self::getMethodParametersForCall($method)</code>
738739
</MissingThrowsDocblock>
739740
<PossiblyNullReference occurrences="1">
740741
<code>allowsNull</code>
@@ -956,23 +957,30 @@
956957
<DocblockTypeContradiction occurrences="1">
957958
<code>$this-&gt;topTestSuite === null</code>
958959
</DocblockTypeContradiction>
960+
<InvalidArgument occurrences="1">
961+
<code>[$test, 'runBare']</code>
962+
</InvalidArgument>
963+
<InvalidCatch occurrences="1"/>
959964
<MissingConstructor occurrences="2">
960965
<code>$codeCoverage</code>
961966
<code>$topTestSuite</code>
962967
</MissingConstructor>
963-
<PossiblyUndefinedVariable occurrences="2">
968+
<PossiblyInvalidArgument occurrences="1">
969+
<code>$linesToBeCovered</code>
970+
</PossiblyInvalidArgument>
971+
<PossiblyUndefinedVariable occurrences="3">
972+
<code>$_timeout</code>
964973
<code>$_timeout</code>
965974
<code>$isAnyCoverageRequired</code>
966975
</PossiblyUndefinedVariable>
967976
<RedundantConditionGivenDocblockType occurrences="2">
968977
<code>$this-&gt;codeCoverage !== null</code>
969978
<code>$this-&gt;codeCoverage !== null</code>
970979
</RedundantConditionGivenDocblockType>
971-
<UndefinedClass occurrences="2">
972-
<code>Invoker</code>
973-
<code>TimeoutException</code>
974-
</UndefinedClass>
975-
<UndefinedInterfaceMethod occurrences="4">
980+
<UndefinedInterfaceMethod occurrences="7">
981+
<code>addToAssertionCount</code>
982+
<code>addToAssertionCount</code>
983+
<code>getNumAssertions</code>
976984
<code>getSize</code>
977985
<code>getSize</code>
978986
<code>getSize</code>
@@ -1213,9 +1221,6 @@
12131221
<DocblockTypeContradiction occurrences="1">
12141222
<code>self::$directories === null</code>
12151223
</DocblockTypeContradiction>
1216-
<UndefinedClass occurrences="1">
1217-
<code>Invoker</code>
1218-
</UndefinedClass>
12191224
</file>
12201225
<file src="src/Util/Color.php">
12211226
<MissingClosureParamType occurrences="2">

src/Framework/MockObject/Builder/Match.php renamed to src/Framework/MockObject/Builder/Match_.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
/**
1313
* @internal This class is not covered by the backward compatibility promise for PHPUnit
1414
*/
15-
interface Match extends Stub
15+
interface Match_ extends Stub
1616
{
1717
/**
1818
* Defines the expectation which must occur before the current is valid.

src/Framework/MockObject/Builder/ParametersMatch.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
/**
1313
* @internal This class is not covered by the backward compatibility promise for PHPUnit
1414
*/
15-
interface ParametersMatch extends Match
15+
interface ParametersMatch extends Match_
1616
{
1717
/**
1818
* Sets the parameters to match for, each parameter to this function will

src/Framework/MockObject/MockMethod.php

Lines changed: 59 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ public static function fromReflection(\ReflectionMethod $method, bool $callOrigi
121121
$method->getName(),
122122
$cloneArguments,
123123
$modifier,
124-
self::getMethodParameters($method),
125-
self::getMethodParameters($method, true),
124+
self::getMethodParametersForDeclaration($method),
125+
self::getMethodParametersForCall($method),
126126
self::deriveReturnType($method),
127127
$reference,
128128
$callOriginalMethod,
@@ -244,7 +244,7 @@ private function getTemplate(string $template): \Text_Template
244244
*
245245
* @throws RuntimeException
246246
*/
247-
private static function getMethodParameters(\ReflectionMethod $method, bool $forCall = false): string
247+
private static function getMethodParametersForDeclaration(\ReflectionMethod $method): string
248248
{
249249
$parameters = [];
250250

@@ -258,71 +258,38 @@ private static function getMethodParameters(\ReflectionMethod $method, bool $for
258258
$name = '$arg' . $i;
259259
}
260260

261-
if ($parameter->isVariadic()) {
262-
if ($forCall) {
263-
continue;
264-
}
265-
266-
$name = '...' . $name;
267-
}
268-
269261
$nullable = '';
270262
$default = '';
271263
$reference = '';
272264
$typeDeclaration = '';
265+
$type = null;
266+
$typeName = null;
273267

274-
if (!$forCall) {
275-
if ($parameter->hasType() && $parameter->allowsNull()) {
276-
$nullable = '?';
268+
if ($parameter->hasType()) {
269+
$type = $parameter->getType();
270+
271+
if ($type instanceof \ReflectionNamedType) {
272+
$typeName = $type->getName();
277273
}
274+
}
278275

279-
if ($parameter->hasType()) {
280-
$type = $parameter->getType();
281-
282-
if ($type instanceof \ReflectionNamedType && $type->getName() !== 'self') {
283-
$typeDeclaration = $type->getName() . ' ';
284-
} else {
285-
try {
286-
$class = $parameter->getClass();
287-
// @codeCoverageIgnoreStart
288-
} catch (\ReflectionException $e) {
289-
throw new RuntimeException(
290-
\sprintf(
291-
'Cannot mock %s::%s() because a class or ' .
292-
'interface used in the signature is not loaded',
293-
$method->getDeclaringClass()->getName(),
294-
$method->getName()
295-
),
296-
0,
297-
$e
298-
);
299-
}
300-
// @codeCoverageIgnoreEnd
301-
302-
if ($class !== null) {
303-
$typeDeclaration = $class->getName() . ' ';
304-
}
305-
}
276+
if ($parameter->isVariadic()) {
277+
$name = '...' . $name;
278+
} elseif ($parameter->isDefaultValueAvailable()) {
279+
$default = ' = ' . \var_export($parameter->getDefaultValue(), true);
280+
} elseif ($parameter->isOptional()) {
281+
$default = ' = null';
282+
}
283+
284+
if ($type !== null) {
285+
if ($typeName !== 'mixed' && $parameter->allowsNull()) {
286+
$nullable = '?';
306287
}
307288

308-
if (!$parameter->isVariadic()) {
309-
if ($parameter->isDefaultValueAvailable()) {
310-
try {
311-
$value = \var_export($parameter->getDefaultValue(), true);
312-
// @codeCoverageIgnoreStart
313-
} catch (\ReflectionException $e) {
314-
throw new RuntimeException(
315-
$e->getMessage(),
316-
(int) $e->getCode(),
317-
$e
318-
);
319-
}
320-
// @codeCoverageIgnoreEnd
321-
322-
$default = ' = ' . $value;
323-
} elseif ($parameter->isOptional()) {
324-
$default = ' = null';
325-
}
289+
if ($typeName === 'self') {
290+
$typeDeclaration = $method->getDeclaringClass()->getName() . ' ';
291+
} elseif ($typeName !== null) {
292+
$typeDeclaration = $typeName . ' ';
326293
}
327294
}
328295

@@ -336,6 +303,39 @@ private static function getMethodParameters(\ReflectionMethod $method, bool $for
336303
return \implode(', ', $parameters);
337304
}
338305

306+
/**
307+
* Returns the parameters of a function or method.
308+
*
309+
* @throws \ReflectionException
310+
*/
311+
private static function getMethodParametersForCall(\ReflectionMethod $method): string
312+
{
313+
$parameters = [];
314+
315+
foreach ($method->getParameters() as $i => $parameter) {
316+
$name = '$' . $parameter->getName();
317+
318+
/* Note: PHP extensions may use empty names for reference arguments
319+
* or "..." for methods taking a variable number of arguments.
320+
*/
321+
if ($name === '$' || $name === '$...') {
322+
$name = '$arg' . $i;
323+
}
324+
325+
if ($parameter->isVariadic()) {
326+
continue;
327+
}
328+
329+
if ($parameter->isPassedByReference()) {
330+
$parameters[] = '&' . $name;
331+
} else {
332+
$parameters[] = $name;
333+
}
334+
}
335+
336+
return \implode(', ', $parameters);
337+
}
338+
339339
private static function deriveReturnType(\ReflectionMethod $method): Type
340340
{
341341
$returnType = $method->getReturnType();

src/Runner/PhptTestCase.php

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,6 @@
2929
*/
3030
final class PhptTestCase implements SelfDescribing, Test
3131
{
32-
/**
33-
* @var string[]
34-
*/
35-
private const SETTINGS = [
36-
'allow_url_fopen=1',
37-
'auto_append_file=',
38-
'auto_prepend_file=',
39-
'disable_functions=',
40-
'display_errors=1',
41-
'docref_ext=.html',
42-
'docref_root=',
43-
'error_append_string=',
44-
'error_prepend_string=',
45-
'error_reporting=-1',
46-
'html_errors=0',
47-
'log_errors=0',
48-
'magic_quotes_runtime=0',
49-
'open_basedir=',
50-
'output_buffering=Off',
51-
'output_handler=',
52-
'report_memleaks=0',
53-
'report_zend_debug=0',
54-
'safe_mode=0',
55-
'xdebug.default_enable=0',
56-
];
57-
5832
/**
5933
* @var string
6034
*/
@@ -127,7 +101,7 @@ public function run(TestResult $result = null): TestResult
127101

128102
$code = $this->render($sections['FILE']);
129103
$xfail = false;
130-
$settings = $this->parseIniSection(self::SETTINGS);
104+
$settings = $this->parseIniSection($this->settings($result->getCollectCodeCoverageInformation()));
131105

132106
$result->startTest($this);
133107

@@ -215,7 +189,7 @@ public function run(TestResult $result = null): TestResult
215189
$result->addFailure($this, new IncompleteTestError('XFAIL section but test passes'), $time);
216190
}
217191

218-
$this->runClean($sections);
192+
$this->runClean($sections, $result->getCollectCodeCoverageInformation());
219193

220194
$result->endTest($this, $time);
221195

@@ -376,15 +350,15 @@ private function runSkip(array &$sections, TestResult $result, array $settings):
376350
return false;
377351
}
378352

379-
private function runClean(array &$sections): void
353+
private function runClean(array &$sections, bool $collectCoverage): void
380354
{
381355
$this->phpUtil->setStdin('');
382356
$this->phpUtil->setArgs('');
383357

384358
if (isset($sections['CLEAN'])) {
385359
$cleanCode = $this->render($sections['CLEAN']);
386360

387-
$this->phpUtil->runJob($cleanCode, self::SETTINGS);
361+
$this->phpUtil->runJob($cleanCode, $this->settings($collectCoverage));
388362
}
389363
}
390364

@@ -756,4 +730,56 @@ private function getLocationHint(string $needle, array $sections, ?string $secti
756730
'line' => 1,
757731
]];
758732
}
733+
734+
/**
735+
* @psalm-return list<string>
736+
*/
737+
private function settings(bool $collectCoverage): array
738+
{
739+
$settings = [
740+
'allow_url_fopen=1',
741+
'auto_append_file=',
742+
'auto_prepend_file=',
743+
'disable_functions=',
744+
'display_errors=1',
745+
'docref_ext=.html',
746+
'docref_root=',
747+
'error_append_string=',
748+
'error_prepend_string=',
749+
'error_reporting=-1',
750+
'html_errors=0',
751+
'log_errors=0',
752+
'open_basedir=',
753+
'output_buffering=Off',
754+
'output_handler=',
755+
'report_memleaks=0',
756+
'report_zend_debug=0',
757+
];
758+
759+
if (\extension_loaded('pcov')) {
760+
if ($collectCoverage) {
761+
$settings[] = 'pcov.enabled=1';
762+
} else {
763+
$settings[] = 'pcov.enabled=0';
764+
}
765+
}
766+
767+
if (\extension_loaded('xdebug')) {
768+
if (\version_compare(\phpversion('xdebug'), '3', '>=')) {
769+
if ($collectCoverage) {
770+
$settings[] = 'xdebug.mode=coverage';
771+
} else {
772+
$settings[] = 'xdebug.mode=off';
773+
}
774+
} else {
775+
if ($collectCoverage) {
776+
$settings[] = 'xdebug.coverage_enable=1';
777+
} else {
778+
$settings[] = 'xdebug.default_enable=0';
779+
}
780+
}
781+
}
782+
783+
return $settings;
784+
}
759785
}

0 commit comments

Comments
 (0)