Skip to content

Commit cf4b347

Browse files
[ErrorHandler] trigger deprecations for @final properties
1 parent d88c30d commit cf4b347

File tree

14 files changed

+103
-40
lines changed

14 files changed

+103
-40
lines changed

UPGRADE-6.1.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
UPGRADE FROM 6.0 to 6.1
22
=======================
33

4+
All components
5+
--------------
6+
7+
* Non-static public and protected properties are now considered final
8+
49
Serializer
510
----------
611

src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ public function testTooLongNamespace()
7979

8080
abstract class MaxIdLengthAdapter extends AbstractAdapter
8181
{
82-
protected $maxIdLength = 50;
83-
8482
public function __construct(string $ns)
8583
{
84+
$this->maxIdLength = 50;
85+
8686
parent::__construct($ns);
8787
}
8888
}

src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -413,16 +413,6 @@ class ProjectServiceContainer extends Container
413413
public $__foo_bar;
414414
public $__foo_baz;
415415
public $__internal;
416-
protected $privates;
417-
protected $methodMap = [
418-
'bar' => 'getBarService',
419-
'foo_bar' => 'getFooBarService',
420-
'foo.baz' => 'getFoo_BazService',
421-
'circular' => 'getCircularService',
422-
'throw_exception' => 'getThrowExceptionService',
423-
'throws_exception_on_service_configuration' => 'getThrowsExceptionOnServiceConfigurationService',
424-
'internal_dependency' => 'getInternalDependencyService',
425-
];
426416

427417
public function __construct()
428418
{
@@ -434,6 +424,15 @@ public function __construct()
434424
$this->__internal = new \stdClass();
435425
$this->privates = [];
436426
$this->aliases = ['alias' => 'bar'];
427+
$this->methodMap = [
428+
'bar' => 'getBarService',
429+
'foo_bar' => 'getFooBarService',
430+
'foo.baz' => 'getFoo_BazService',
431+
'circular' => 'getCircularService',
432+
'throw_exception' => 'getThrowExceptionService',
433+
'throws_exception_on_service_configuration' => 'getThrowsExceptionOnServiceConfigurationService',
434+
'internal_dependency' => 'getInternalDependencyService',
435+
];
437436
}
438437

439438
protected function getInternalService()

src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function testRegisterClasses()
8989
$container = new ContainerBuilder();
9090
$container->setParameter('sub_dir', 'Sub');
9191
$loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures'));
92-
$loader->autoRegisterAliasesForSinglyImplementedInterfaces = false;
92+
$loader->noAutoRegisterAliasesForSinglyImplementedInterfaces();
9393

9494
$loader->registerClasses(new Definition(), 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\\', 'Prototype/%sub_dir%/*');
9595
$loader->registerClasses(new Definition(), 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\\', 'Prototype/%sub_dir%/*'); // loading twice should not be an issue
@@ -270,7 +270,10 @@ public function testRegisterClassesWithWhenEnv(?string $env, bool $expected)
270270

271271
class TestFileLoader extends FileLoader
272272
{
273-
public $autoRegisterAliasesForSinglyImplementedInterfaces = true;
273+
public function noAutoRegisterAliasesForSinglyImplementedInterfaces()
274+
{
275+
$this->autoRegisterAliasesForSinglyImplementedInterfaces = false;
276+
}
274277

275278
public function load(mixed $resource, string $type = null): mixed
276279
{

src/Symfony/Component/ErrorHandler/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ CHANGELOG
44
6.1
55
---
66

7-
* Report overridden `@final` constants
7+
* Report overridden `@final` constants and properties
88

99
5.4
1010
---

src/Symfony/Component/ErrorHandler/DebugClassLoader.php

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class DebugClassLoader
112112
private static array $checkedClasses = [];
113113
private static array $final = [];
114114
private static array $finalMethods = [];
115+
private static array $finalProperties = [];
115116
private static array $finalConstants = [];
116117
private static array $deprecated = [];
117118
private static array $internal = [];
@@ -469,9 +470,10 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
469470
self::$finalMethods[$class] = [];
470471
self::$internalMethods[$class] = [];
471472
self::$annotatedParameters[$class] = [];
473+
self::$finalProperties[$class] = [];
472474
self::$finalConstants[$class] = [];
473475
foreach ($parentAndOwnInterfaces as $use) {
474-
foreach (['finalMethods', 'internalMethods', 'annotatedParameters', 'returnTypes', 'finalConstants'] as $property) {
476+
foreach (['finalMethods', 'internalMethods', 'annotatedParameters', 'returnTypes', 'finalProperties', 'finalConstants'] as $property) {
475477
if (isset(self::${$property}[$use])) {
476478
self::${$property}[$class] = self::${$property}[$class] ? self::${$property}[$use] + self::${$property}[$class] : self::${$property}[$use];
477479
}
@@ -626,22 +628,29 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array
626628
}
627629
}
628630

629-
foreach ($refl->getReflectionConstants(\ReflectionClassConstant::IS_PUBLIC | \ReflectionClassConstant::IS_PROTECTED) as $constant) {
630-
if ($constant->class !== $class) {
631-
continue;
632-
}
631+
$finals = isset(self::$final[$class]) || $refl->isFinal() ? [] : [
632+
'finalConstants' => $refl->getReflectionConstants(\ReflectionClassConstant::IS_PUBLIC | \ReflectionClassConstant::IS_PROTECTED),
633+
'finalProperties' => $refl->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED),
634+
];
635+
foreach ($finals as $type => $reflectors) {
636+
foreach ($reflectors as $r) {
637+
if ($r->class !== $class) {
638+
continue;
639+
}
633640

634-
foreach ($parentAndOwnInterfaces as $use) {
635-
if (isset(self::$finalConstants[$use][$constant->name])) {
636-
$deprecations[] = sprintf('The "%s::%s" constant is considered final. You should not override it in "%s".', self::$finalConstants[$use][$constant->name], $constant->name, $class);
641+
foreach ($parentAndOwnInterfaces as $use) {
642+
if (isset(self::${$type}[$use][$r->name]) && ('finalConstants' === $type || substr($use, 0, strrpos($use, '\\')) !== substr($use, 0, strrpos($class, '\\')))) {
643+
$msg = 'finalConstants' === $type ? '%s" constant' : '$%s" property';
644+
$deprecations[] = sprintf('The "%s::'.$msg.' is considered final. You should not override it in "%s".', self::${$type}[$use][$r->name], $r->name, $class);
645+
}
637646
}
638-
}
639647

640-
if (!($doc = $this->parsePhpDoc($constant)) || !isset($doc['final'])) {
641-
continue;
642-
}
648+
$doc = $this->parsePhpDoc($r);
643649

644-
self::$finalConstants[$class][$constant->name] = $class;
650+
if (isset($doc['final']) || ('finalProperties' === $type && str_starts_with($class, 'Symfony\\') && !$r->isStatic() && !$r->hasType())) {
651+
self::${$type}[$class][$r->name] = $class;
652+
}
653+
}
645654
}
646655

647656
return $deprecations;

src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,13 +401,30 @@ class_exists('Test\\'.ReturnType::class, true);
401401
], $deprecations);
402402
}
403403

404+
public function testOverrideFinalProperty()
405+
{
406+
$deprecations = [];
407+
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
408+
$e = error_reporting(E_USER_DEPRECATED);
409+
410+
class_exists(Fixtures\OverrideFinalProperty::class, true);
411+
412+
error_reporting($e);
413+
restore_error_handler();
414+
415+
$this->assertSame([
416+
'The "Symfony\Component\ErrorHandler\Tests\Fixtures\FinalProperty\FinalProperty::$pub" property is considered final. You should not override it in "Symfony\Component\ErrorHandler\Tests\Fixtures\OverrideFinalProperty".',
417+
'The "Symfony\Component\ErrorHandler\Tests\Fixtures\FinalProperty\FinalProperty::$prot" property is considered final. You should not override it in "Symfony\Component\ErrorHandler\Tests\Fixtures\OverrideFinalProperty".',
418+
], $deprecations);
419+
}
420+
404421
public function testOverrideFinalConstant()
405422
{
406423
$deprecations = [];
407424
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
408425
$e = error_reporting(E_USER_DEPRECATED);
409426

410-
class_exists( Fixtures\FinalConstant\OverrideFinalConstant::class, true);
427+
class_exists(Fixtures\FinalConstant\OverrideFinalConstant::class, true);
411428

412429
error_reporting($e);
413430
restore_error_handler();
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Symfony\Component\ErrorHandler\Tests\Fixtures\FinalProperty;
4+
5+
class FinalProperty
6+
{
7+
/**
8+
* @final
9+
*/
10+
public $pub;
11+
12+
/**
13+
* @final
14+
*/
15+
protected $prot;
16+
17+
/**
18+
* @final
19+
*/
20+
private $priv;
21+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Symfony\Component\ErrorHandler\Tests\Fixtures;
4+
5+
use Symfony\Component\ErrorHandler\Tests\Fixtures\FinalProperty\FinalProperty;
6+
7+
class OverrideFinalProperty extends FinalProperty
8+
{
9+
public $pub;
10+
protected $prot;
11+
private $priv;
12+
}

src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
* A test case to ease testing Constraint Validators.
4040
*
4141
* @author Bernhard Schussek <bschussek@gmail.com>
42+
*
43+
* @template T of ConstraintValidatorInterface
4244
*/
4345
abstract class ConstraintValidatorTestCase extends TestCase
4446
{
@@ -48,7 +50,7 @@ abstract class ConstraintValidatorTestCase extends TestCase
4850
protected $context;
4951

5052
/**
51-
* @var ConstraintValidatorInterface
53+
* @var T
5254
*/
5355
protected $validator;
5456

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy