Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 64b68d4

Browse files
committed
bug #31730 [PhpUnitBridge] More accurate grouping (greg0ire)
This PR was squashed before being merged into the 4.3 branch (closes #31730). Discussion ---------- [PhpUnitBridge] More accurate grouping | Q | A | ------------- | --- | Branch? | 4.3 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | n/a Sometimes, you cannot easily know if code was written by a vendor or directly in the application, for instance if the code comes from a file in the cache. In that case, it is better not to classify the deprecation as direct or indirect. @jmsche please test this on your application when you can, I think you might be having that issue. Commits ------- d9f0ba3 [PhpUnitBridge] More accurate grouping
2 parents 3cd795f + d9f0ba3 commit 64b68d4

File tree

9 files changed

+114
-69
lines changed

9 files changed

+114
-69
lines changed

src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,13 @@ public function handleError($type, $msg, $file, $line, $context = [])
139139
$group = 'unsilenced';
140140
} elseif ($deprecation->isLegacy(self::$utilPrefix)) {
141141
$group = 'legacy';
142-
} elseif (!$deprecation->isSelf()) {
143-
$group = $deprecation->isIndirect() ? 'remaining indirect' : 'remaining direct';
144142
} else {
145-
$group = 'remaining self';
143+
$group = [
144+
Deprecation::TYPE_SELF => 'remaining self',
145+
Deprecation::TYPE_DIRECT => 'remaining direct',
146+
Deprecation::TYPE_INDIRECT => 'remaining indirect',
147+
Deprecation::TYPE_UNDETERMINED => 'other',
148+
][$deprecation->getType()];
146149
}
147150

148151
if ($this->getConfiguration()->shouldDisplayStackTrace($msg)) {

src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
*/
1919
class Deprecation
2020
{
21+
private const PATH_TYPE_VENDOR = 'path_type_vendor';
22+
private const PATH_TYPE_SELF = 'path_type_internal';
23+
private const PATH_TYPE_UNDETERMINED = 'path_type_undetermined';
24+
25+
public const TYPE_SELF = 'type_self';
26+
public const TYPE_DIRECT = 'type_direct';
27+
public const TYPE_INDIRECT = 'type_indirect';
28+
public const TYPE_UNDETERMINED = 'type_undetermined';
29+
2130
/**
2231
* @var array
2332
*/
@@ -39,13 +48,21 @@ class Deprecation
3948
private $originMethod;
4049

4150
/**
42-
* @var bool
51+
* @var string one of the PATH_TYPE_* constants
4352
*/
44-
private $self;
53+
private $triggeringFilePathType;
4554

4655
/** @var string[] absolute paths to vendor directories */
4756
private static $vendors;
4857

58+
/**
59+
* @var string[] absolute paths to source or tests of the project. This
60+
* excludes cache directories, because it is based on
61+
* autoloading rules and cache systems typically do not use
62+
* those.
63+
*/
64+
private static $internalPaths;
65+
4966
/**
5067
* @param string $message
5168
* @param string $file
@@ -59,7 +76,7 @@ public function __construct($message, array $trace, $file)
5976
// No-op
6077
}
6178
$line = $trace[$i];
62-
$this->self = !$this->pathOriginatesFromVendor($file);
79+
$this->trigerringFilePathType = $this->getPathType($file);
6380
if (isset($line['object']) || isset($line['class'])) {
6481
if (isset($line['class']) && 0 === strpos($line['class'], SymfonyTestsListenerFor::class)) {
6582
$parsedMsg = unserialize($this->message);
@@ -70,8 +87,9 @@ public function __construct($message, array $trace, $file)
7087
// \Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerTrait::endTest()
7188
// then we need to use the serialized information to determine
7289
// if the error has been triggered from vendor code.
73-
$this->self = isset($parsedMsg['triggering_file'])
74-
&& $this->pathOriginatesFromVendor($parsedMsg['triggering_file']);
90+
if (isset($parsedMsg['triggering_file'])) {
91+
$this->trigerringFilePathType = $this->getPathType($parsedMsg['triggering_file']);
92+
}
7593

7694
return;
7795
}
@@ -101,14 +119,6 @@ public function originatesFromAnObject()
101119
return isset($this->originClass);
102120
}
103121

104-
/**
105-
* @return bool
106-
*/
107-
public function isSelf()
108-
{
109-
return $this->self;
110-
}
111-
112122
/**
113123
* @return string
114124
*/
@@ -163,10 +173,16 @@ public function isLegacy($utilPrefix)
163173
* Tells whether both the calling package and the called package are vendor
164174
* packages.
165175
*
166-
* @return bool
176+
* @return string
167177
*/
168-
public function isIndirect()
178+
public function getType()
169179
{
180+
if (self::PATH_TYPE_SELF === $this->trigerringFilePathType) {
181+
return self::TYPE_SELF;
182+
}
183+
if (self::PATH_TYPE_UNDETERMINED === $this->trigerringFilePathType) {
184+
return self::TYPE_UNDETERMINED;
185+
}
170186
$erroringFile = $erroringPackage = null;
171187
foreach ($this->trace as $line) {
172188
if (\in_array($line['function'], ['require', 'require_once', 'include', 'include_once'], true)) {
@@ -179,25 +195,28 @@ public function isIndirect()
179195
if ('-' === $file || 'Standard input code' === $file || !realpath($file)) {
180196
continue;
181197
}
182-
if (!$this->pathOriginatesFromVendor($file)) {
183-
return false;
198+
if (self::PATH_TYPE_SELF === $this->getPathType($file)) {
199+
return self::TYPE_DIRECT;
200+
}
201+
if (self::PATH_TYPE_UNDETERMINED === $this->getPathType($file)) {
202+
return self::TYPE_UNDETERMINED;
184203
}
185204
if (null !== $erroringFile && null !== $erroringPackage) {
186205
$package = $this->getPackage($file);
187206
if ('composer' !== $package && $package !== $erroringPackage) {
188-
return true;
207+
return self::TYPE_INDIRECT;
189208
}
190209
continue;
191210
}
192211
$erroringFile = $file;
193212
$erroringPackage = $this->getPackage($file);
194213
}
195214

196-
return false;
215+
return self::TYPE_DIRECT;
197216
}
198217

199218
/**
200-
* pathOriginatesFromVendor() should always be called prior to calling this method.
219+
* getPathType() should always be called prior to calling this method.
201220
*
202221
* @param string $path
203222
*
@@ -237,6 +256,15 @@ private static function getVendors()
237256
$v = \dirname(\dirname($r->getFileName()));
238257
if (file_exists($v.'/composer/installed.json')) {
239258
self::$vendors[] = $v;
259+
$loader = require $v.'/autoload.php';
260+
$paths = self::getSourcePathsFromPrefixes(array_merge($loader->getPrefixes(), $loader->getPrefixesPsr4()));
261+
}
262+
}
263+
}
264+
foreach ($paths as $path) {
265+
foreach (self::$vendors as $vendor) {
266+
if (0 !== strpos($path, $vendor)) {
267+
self::$internalPaths[] = $path;
240268
}
241269
}
242270
}
@@ -245,24 +273,41 @@ private static function getVendors()
245273
return self::$vendors;
246274
}
247275

276+
private static function getSourcePathsFromPrefixes(array $prefixesByNamespace)
277+
{
278+
foreach ($prefixesByNamespace as $prefixes) {
279+
foreach ($prefixes as $prefix) {
280+
if (false !== realpath($prefix)) {
281+
yield realpath($prefix);
282+
}
283+
}
284+
}
285+
}
286+
248287
/**
249288
* @param string $path
250289
*
251-
* @return bool
290+
* @return string
252291
*/
253-
private function pathOriginatesFromVendor($path)
292+
private function getPathType($path)
254293
{
255294
$realPath = realpath($path);
256295
if (false === $realPath && '-' !== $path && 'Standard input code' !== $path) {
257-
return true;
296+
return self::PATH_TYPE_UNDETERMINED;
258297
}
259298
foreach (self::getVendors() as $vendor) {
260299
if (0 === strpos($realPath, $vendor) && false !== strpbrk(substr($realPath, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
261-
return true;
300+
return self::PATH_TYPE_VENDOR;
262301
}
263302
}
264303

265-
return false;
304+
foreach (self::$internalPaths as $internalPath) {
305+
if (0 === strpos($realPath, $internalPath)) {
306+
return self::PATH_TYPE_SELF;
307+
}
308+
}
309+
310+
return self::PATH_TYPE_UNDETERMINED;
266311
}
267312

268313
/**
@@ -281,19 +326,4 @@ public function toString()
281326
"\n".str_replace(' '.getcwd().\DIRECTORY_SEPARATOR, ' ', $exception->getTraceAsString()).
282327
"\n";
283328
}
284-
285-
private function getPackageFromLine(array $line)
286-
{
287-
if (!isset($line['file'])) {
288-
return 'internal function';
289-
}
290-
if (!$this->pathOriginatesFromVendor($line['file'])) {
291-
return 'source code';
292-
}
293-
try {
294-
return $this->getPackage($line['file']);
295-
} catch (\RuntimeException $e) {
296-
return 'unknown';
297-
}
298-
}
299329
}

src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function testItCanDetermineTheClassWhereTheDeprecationHappened()
2727
public function testItCanTellWhetherItIsInternal()
2828
{
2929
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
30-
$this->assertTrue($deprecation->isSelf());
30+
$this->assertSame(Deprecation::TYPE_SELF, $deprecation->getType());
3131
}
3232

3333
public function testLegacyTestMethodIsDetectedAsSuch()
@@ -46,7 +46,7 @@ public function testItCanBeConvertedToAString()
4646
public function testItRulesOutFilesOutsideVendorsAsIndirect()
4747
{
4848
$deprecation = new Deprecation('💩', $this->debugBacktrace(), __FILE__);
49-
$this->assertFalse($deprecation->isIndirect());
49+
$this->assertNotSame(Deprecation::TYPE_INDIRECT, $deprecation->getType());
5050
}
5151

5252
/**

src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/default.phpt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,13 @@ Unsilenced deprecation notices (3)
7373
1x: unsilenced bar deprecation
7474
1x in FooTestCase::testNonLegacyBar
7575

76-
Remaining self deprecation notices (1)
77-
78-
1x: silenced bar deprecation
79-
1x in FooTestCase::testNonLegacyBar
80-
8176
Legacy deprecation notices (1)
8277

83-
Other deprecation notices (1)
78+
Other deprecation notices (2)
8479

8580
1x: root deprecation
8681

82+
1x: silenced bar deprecation
83+
1x in FooTestCase::testNonLegacyBar
84+
8785
I get precedence over any exit statements inside the deprecation error handler.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
<?php
22

33
require_once __DIR__.'/composer/autoload_real.php';
4+
5+
return ComposerAutoloaderInitFake::getLoader();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
<?php
22

3+
class ComposerLoaderFake
4+
{
5+
public function getPrefixes()
6+
{
7+
return [];
8+
}
9+
10+
public function getPrefixesPsr4()
11+
{
12+
return [];
13+
}
14+
}
15+
316
class ComposerAutoloaderInitFake
417
{
18+
public static function getLoader()
19+
{
20+
return new ComposerLoaderFake();
21+
}
522
}

src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/self_on_non_vendor.phpt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,12 @@ Unsilenced deprecation notices (3)
6161
1x: unsilenced bar deprecation
6262
1x in FooTestCase::testNonLegacyBar
6363

64-
Remaining self deprecation notices (1)
65-
66-
1x: silenced bar deprecation
67-
1x in FooTestCase::testNonLegacyBar
68-
6964
Legacy deprecation notices (1)
7065

71-
Other deprecation notices (1)
66+
Other deprecation notices (2)
7267

7368
1x: root deprecation
7469

70+
1x: silenced bar deprecation
71+
1x in FooTestCase::testNonLegacyBar
72+

src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/shutdown_deprecations.phpt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,15 @@ Unsilenced deprecation notices (3)
7373
1x: unsilenced bar deprecation
7474
1x in FooTestCase::testNonLegacyBar
7575

76-
Remaining self deprecation notices (1)
77-
78-
1x: silenced bar deprecation
79-
1x in FooTestCase::testNonLegacyBar
80-
8176
Legacy deprecation notices (1)
8277

83-
Other deprecation notices (1)
78+
Other deprecation notices (2)
8479

8580
1x: root deprecation
8681

82+
1x: silenced bar deprecation
83+
1x in FooTestCase::testNonLegacyBar
84+
8785
Shutdown-time deprecations:
8886

8987
Other deprecation notices (1)

src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/weak_vendors_on_non_vendor.phpt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,13 @@ Unsilenced deprecation notices (3)
6161
1x: unsilenced bar deprecation
6262
1x in FooTestCase::testNonLegacyBar
6363

64-
Remaining self deprecation notices (1)
65-
66-
1x: silenced bar deprecation
67-
1x in FooTestCase::testNonLegacyBar
68-
6964
Legacy deprecation notices (1)
7065

71-
Other deprecation notices (1)
66+
Other deprecation notices (2)
7267

7368
1x: root deprecation
7469

70+
1x: silenced bar deprecation
71+
1x in FooTestCase::testNonLegacyBar
72+
73+

0 commit comments

Comments
 (0)