Skip to content

Commit 816a70b

Browse files
authored
Merge pull request #24 from doctrine/SimplifyModes
Simplify modes, allow stacking them, no tracking by default. Improve README
2 parents d7eade7 + d3ff930 commit 816a70b

File tree

6 files changed

+210
-173
lines changed

6 files changed

+210
-173
lines changed

README.md

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,76 @@
11
# Doctrine Deprecations
22

3-
A small layer on top of `trigger_error(E_USER_DEPRECATED)` or PSR-3 logging
4-
with options to disable all deprecations or selectively for packages.
3+
A small (side-effect free by default) layer on top of
4+
`trigger_error(E_USER_DEPRECATED)` or PSR-3 logging.
55

6-
By default it does not log deprecations at runtime and needs to be configured
7-
to log through either trigger_error or with a PSR-3 logger. This is done to
8-
avoid side effects by deprecations on user error handlers that Doctrine has no
9-
control over.
6+
- no side-effects by default, making it a perfect fit for libraries that don't know how the error handler works they operate under
7+
- options to avoid having to rely on error handlers global state by using PSR-3 logging
8+
- deduplicate deprecation messages to avoid excessive triggering and reduce overhead
9+
10+
We recommend to collect Deprecations using a PSR logger instead of relying on
11+
the global error handler.
1012

1113
## Usage from consumer perspective:
1214

13-
Enable or Disable Doctrine deprecations to be sent as `trigger_error(E_USER_DEPRECATED)`
15+
Enable Doctrine deprecations to be sent to a PSR3 logger:
16+
17+
```php
18+
\Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
19+
```
20+
21+
Enable Doctrine deprecations to be sent as `@trigger_error($message, E_USER_DEPRECATED)`
1422
messages.
1523

1624
```php
1725
\Doctrine\Deprecations\Deprecation::enableWithTriggerError();
18-
\Doctrine\Deprecations\Deprecation::enableWithSuppressedTriggerError();
19-
\Doctrine\Deprecations\Deprecation::disable();
2026
```
2127

22-
Enable Doctrine deprecations to be sent to a PSR3 logger:
28+
If you only want to enable deprecation tracking, without logging or calling `trigger_error` then call:
2329

2430
```php
25-
\Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
31+
\Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
2632
```
2733

28-
Disable deprecations from a package
34+
Tracking is enabled with all three modes and provides access to all triggered
35+
deprecations and their individual count:
2936

3037
```php
31-
\Doctrine\Deprecations\Deprecation::ignorePackage("doctrine/orm");
38+
$deprecations = \Doctrine\Deprecations\Deprecation::getTriggeredDeprecations();
39+
40+
foreach ($deprecations as $identifier => $count) {
41+
echo $identifier . " was triggered " . $count . " times\n";
42+
}
3243
```
3344

45+
### Suppressing Specific Deprecations
46+
3447
Disable triggering about specific deprecations:
3548

3649
```php
3750
\Doctrine\Deprecations\Deprecation::ignoreDeprecations("https://link/to/deprecations-description-identifier");
3851
```
3952

40-
Access is provided to all triggered deprecations and their individual count:
53+
Disable all deprecations from a package
4154

4255
```php
43-
$deprecations = \Doctrine\Deprecations\Deprecation::getTriggeredDeprecations();
44-
45-
foreach ($deprecations as $identifier => $count) {
46-
echo $identifier . " was triggered " . $count . " times\n";
47-
}
56+
\Doctrine\Deprecations\Deprecation::ignorePackage("doctrine/orm");
4857
```
4958

59+
### Other Operations
60+
5061
When used within PHPUnit or other tools that could collect multiple instances of the same deprecations
5162
the deduplication can be disabled:
5263

5364
```php
5465
\Doctrine\Deprecations\Deprecation::withoutDeduplication();
5566
```
5667

68+
Disable deprecation tracking again:
69+
70+
```php
71+
\Doctrine\Deprecations\Deprecation::disable();
72+
```
73+
5774
## Usage from a library/producer perspective:
5875

5976
When you want to unconditionally trigger a deprecation even when called
@@ -93,7 +110,7 @@ then use:
93110
```
94111

95112
Based on the issue link each deprecation message is only triggered once per
96-
request, so it must be unique for each deprecation.
113+
request.
97114

98115
A limited stacktrace is included in the deprecation message to find the
99116
offending location.

lib/Doctrine/Deprecations/Deprecation.php

Lines changed: 76 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,43 @@
88

99
use function array_key_exists;
1010
use function array_reduce;
11-
use function basename;
1211
use function debug_backtrace;
1312
use function sprintf;
1413
use function strpos;
14+
use function strrpos;
15+
use function substr;
1516
use function trigger_error;
1617

1718
use const DEBUG_BACKTRACE_IGNORE_ARGS;
19+
use const DIRECTORY_SEPARATOR;
1820
use const E_USER_DEPRECATED;
1921

2022
/**
2123
* Manages Deprecation logging in different ways.
2224
*
23-
* By default triggered exceptions are not logged, only the amount of
24-
* deprecations triggered can be queried with `Deprecation::getUniqueTriggeredDeprecationsCount()`.
25+
* By default triggered exceptions are not logged.
2526
*
2627
* To enable different deprecation logging mechanisms you can call the
2728
* following methods:
2829
*
29-
* - Uses trigger_error with E_USER_DEPRECATED
30-
* \Doctrine\Deprecations\Deprecation::enableWithTriggerError();
30+
* - Minimal collection of deprecations via getTriggeredDeprecations()
31+
* \Doctrine\Deprecations\Deprecation::enableTrackingDeprecations();
3132
*
3233
* - Uses @trigger_error with E_USER_DEPRECATED
33-
* \Doctrine\Deprecations\Deprecation::enableWithSuppressedTriggerError();
34+
* \Doctrine\Deprecations\Deprecation::enableWithTriggerError();
3435
*
3536
* - Sends deprecation messages via a PSR-3 logger
3637
* \Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger);
3738
*
38-
* Packages that trigger deprecations should use the `trigger()` method.
39+
* Packages that trigger deprecations should use the `trigger()` or
40+
* `triggerIfCalledFromOutside()` methods.
3941
*/
4042
class Deprecation
4143
{
42-
private const TYPE_NONE = 0;
43-
private const TYPE_TRIGGER_ERROR = 1;
44-
private const TYPE_TRIGGER_SUPPRESSED_ERROR = 2;
45-
private const TYPE_PSR_LOGGER = 3;
44+
private const TYPE_NONE = 0;
45+
private const TYPE_TRACK_DEPRECATIONS = 1;
46+
private const TYPE_TRIGGER_ERROR = 2;
47+
private const TYPE_PSR_LOGGER = 4;
4648

4749
/** @var int */
4850
private static $type = self::TYPE_NONE;
@@ -60,7 +62,7 @@ class Deprecation
6062
private static $deduplication = true;
6163

6264
/**
63-
* Trigger a deprecation for the given package, starting with given version.
65+
* Trigger a deprecation for the given package and identfier.
6466
*
6567
* The link should point to a Github issue or Wiki entry detailing the
6668
* deprecation. It is additionally used to de-duplicate the trigger of the
@@ -70,6 +72,10 @@ class Deprecation
7072
*/
7173
public static function trigger(string $package, string $link, string $message, ...$args): void
7274
{
75+
if (self::$type === self::TYPE_NONE) {
76+
return;
77+
}
78+
7379
if (array_key_exists($link, self::$ignoredLinks)) {
7480
self::$ignoredLinks[$link]++;
7581
} else {
@@ -80,12 +86,6 @@ public static function trigger(string $package, string $link, string $message, .
8086
return;
8187
}
8288

83-
// do not move this condition to the top, because we still want to
84-
// count occcurences of deprecations even when we are not logging them.
85-
if (self::$type === self::TYPE_NONE) {
86-
return;
87-
}
88-
8989
if (isset(self::$ignoredPackages[$package])) {
9090
return;
9191
}
@@ -98,16 +98,32 @@ public static function trigger(string $package, string $link, string $message, .
9898
}
9999

100100
/**
101+
* Trigger a deprecation for the given package and identifier when called from outside.
102+
*
103+
* "Outside" means we assume that $package is currently installed as a
104+
* dependency and the caller is not a file in that package. When $package
105+
* is installed as a root package then deprecations triggered from the
106+
* tests folder are also considered "outside".
107+
*
108+
* This deprecation method assumes that you are using Composer to install
109+
* the dependency and are using the default /vendor/ folder and not a
110+
* Composer plugin to change the install location. The assumption is also
111+
* that $package is the exact composer packge name.
112+
*
113+
* Compared to {@link trigger()} this method causes some overhead when
114+
* deprecation tracking is enabled even during deduplication, because it
115+
* needs to call {@link debug_backtrace()}
116+
*
101117
* @param mixed $args
102118
*/
103119
public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void
104120
{
121+
if (self::$type === self::TYPE_NONE) {
122+
return;
123+
}
124+
105125
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
106126

107-
// "outside" means we assume that $package is currently installed as a
108-
// dependency and the caller is not a file in that package.
109-
// When $package is installed as a root package, then this deprecation
110-
// is always ignored
111127
// first check that the caller is not from a tests folder, in which case we always let deprecations pass
112128
if (strpos($backtrace[1]['file'], '/tests/') === false) {
113129
if (strpos($backtrace[0]['file'], '/vendor/' . $package . '/') === false) {
@@ -129,12 +145,6 @@ public static function triggerIfCalledFromOutside(string $package, string $link,
129145
return;
130146
}
131147

132-
// do not move this condition to the top, because we still want to
133-
// count occcurences of deprecations even when we are not logging them.
134-
if (self::$type === self::TYPE_NONE) {
135-
return;
136-
}
137-
138148
if (isset(self::$ignoredPackages[$package])) {
139149
return;
140150
}
@@ -149,56 +159,61 @@ public static function triggerIfCalledFromOutside(string $package, string $link,
149159
*/
150160
private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void
151161
{
152-
if (self::$type === self::TYPE_TRIGGER_ERROR) {
153-
$message .= sprintf(
154-
' (%s:%d called by %s:%d, %s, package %s)',
155-
basename($backtrace[0]['file']),
156-
$backtrace[0]['line'],
157-
basename($backtrace[1]['file']),
158-
$backtrace[1]['line'],
159-
$link,
160-
$package
161-
);
162-
163-
trigger_error($message, E_USER_DEPRECATED);
164-
} elseif (self::$type === self::TYPE_TRIGGER_SUPPRESSED_ERROR) {
165-
$message .= sprintf(
166-
' (%s:%d called by %s:%d, %s, package %s)',
167-
basename($backtrace[0]['file']),
168-
$backtrace[0]['line'],
169-
basename($backtrace[1]['file']),
170-
$backtrace[1]['line'],
171-
$link,
172-
$package
173-
);
174-
175-
@trigger_error($message, E_USER_DEPRECATED);
176-
} elseif (self::$type === self::TYPE_PSR_LOGGER) {
162+
if ((self::$type & self::TYPE_PSR_LOGGER) > 0) {
177163
$context = [
178164
'file' => $backtrace[0]['file'],
179165
'line' => $backtrace[0]['line'],
166+
'package' => $package,
167+
'link' => $link,
180168
];
181169

182-
$context['package'] = $package;
183-
$context['link'] = $link;
184-
185170
self::$logger->notice($message, $context);
186171
}
172+
173+
if (! ((self::$type & self::TYPE_TRIGGER_ERROR) > 0)) {
174+
return;
175+
}
176+
177+
$message .= sprintf(
178+
' (%s:%d called by %s:%d, %s, package %s)',
179+
self::basename($backtrace[0]['file']),
180+
$backtrace[0]['line'],
181+
self::basename($backtrace[1]['file']),
182+
$backtrace[1]['line'],
183+
$link,
184+
$package
185+
);
186+
187+
@trigger_error($message, E_USER_DEPRECATED);
187188
}
188189

189-
public static function enableWithTriggerError(): void
190+
/**
191+
* A non-local-aware version of PHPs basename function.
192+
*/
193+
private static function basename(string $filename): string
190194
{
191-
self::$type = self::TYPE_TRIGGER_ERROR;
195+
$pos = strrpos($filename, DIRECTORY_SEPARATOR);
196+
197+
if ($pos === false) {
198+
return $filename;
199+
}
200+
201+
return substr($filename, $pos + 1);
192202
}
193203

194-
public static function enableWithSuppressedTriggerError(): void
204+
public static function enableTrackingDeprecations(): void
205+
{
206+
self::$type |= self::TYPE_TRACK_DEPRECATIONS;
207+
}
208+
209+
public static function enableWithTriggerError(): void
195210
{
196-
self::$type = self::TYPE_TRIGGER_SUPPRESSED_ERROR;
211+
self::$type |= self::TYPE_TRIGGER_ERROR;
197212
}
198213

199214
public static function enableWithPsrLogger(LoggerInterface $logger): void
200215
{
201-
self::$type = self::TYPE_PSR_LOGGER;
216+
self::$type |= self::TYPE_PSR_LOGGER;
202217
self::$logger = $logger;
203218
}
204219

lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ public function expectNoDeprecationWithIdentifier(string $identifier): void
2626
$this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
2727
}
2828

29+
/**
30+
* @before
31+
*/
32+
public function enableDeprecationTracking(): void
33+
{
34+
Deprecation::enableTrackingDeprecations();
35+
}
36+
2937
/**
3038
* @after
3139
*/

test_fixtures/src/Foo.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88

99
class Foo
1010
{
11-
public function triggerDependencyWithDeprecation(): void
11+
public static function triggerDependencyWithDeprecation(): void
1212
{
1313
$bar = new Bar();
1414
$bar->oldFunc();
1515
}
1616

17-
public function triggerDependencyWithDeprecationFromInside(): void
17+
public static function triggerDependencyWithDeprecationFromInside(): void
1818
{
1919
$bar = new Bar();
2020
$bar->newFunc();

0 commit comments

Comments
 (0)