Completed
Push — master ( ace8e4...b66875 )
by Filip
03:09
created

TranslationExtension::loadConfiguration()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 63
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 63
rs 8.6498
c 0
b 0
f 0
cc 5
eloc 41
nc 4
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of the Kdyby (http://www.kdyby.org)
5
 *
6
 * Copyright (c) 2008 Filip Procházka ([email protected])
7
 *
8
 * For the full copyright and license information, please view the file license.txt that was distributed with this source code.
9
 */
10
11
namespace Kdyby\Translation\DI;
12
13
use Kdyby;
14
use Kdyby\Translation\InvalidResourceException;
15
use Nette;
16
use Nette\DI\Statement;
17
use Nette\PhpGenerator as Code;
18
use Nette\Reflection;
19
use Nette\Utils\Callback;
20
use Nette\Utils\Finder;
21
use Nette\Utils\Strings;
22
use Nette\Utils\Validators;
23
use Symfony\Component\Translation\Loader\LoaderInterface;
24
use Tracy;
25
26
27
28
/**
29
 * @author Filip Procházka <[email protected]>
30
 */
31
class TranslationExtension extends Nette\DI\CompilerExtension
32
{
33
34
	use Kdyby\StrictObjects\Scream;
35
36
	/** @deprecated */
37
	const LOADER_TAG = self::TAG_LOADER;
38
	/** @deprecated */
39
	const DUMPER_TAG = self::TAG_DUMPER;
40
	/** @deprecated */
41
	const EXTRACTOR_TAG = self::TAG_EXTRACTOR;
42
43
	const TAG_LOADER = 'translation.loader';
44
	const TAG_DUMPER = 'translation.dumper';
45
	const TAG_EXTRACTOR = 'translation.extractor';
46
47
	const RESOLVER_REQUEST = 'request';
48
	const RESOLVER_HEADER = 'header';
49
	const RESOLVER_SESSION = 'session';
50
51
	/**
52
	 * @var array
53
	 */
54
	public $defaults = [
55
		'whitelist' => NULL, // array('cs', 'en'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
78% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
56
		'default' => 'en',
57
		'logging' => NULL, //  TRUE for psr/log, or string for kdyby/monolog channel
58
		// 'fallback' => array('en_US', 'en'), // using custom merge strategy becase Nette's config merger appends lists of values
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
59
		'dirs' => ['%appDir%/lang', '%appDir%/locale'],
60
		'cache' => 'Kdyby\Translation\Caching\PhpFileStorage',
61
		'debugger' => '%debugMode%',
62
		'resolvers' => [
63
			self::RESOLVER_SESSION => FALSE,
64
			self::RESOLVER_REQUEST => TRUE,
65
			self::RESOLVER_HEADER => TRUE,
66
		],
67
		'loaders' => []
68
	];
69
70
	/**
71
	 * @var array
72
	 */
73
	private $loaders;
74
75
76
77
	public function __construct()
78
	{
79
		$this->defaults['cache'] = new Statement($this->defaults['cache'], ['%tempDir%/cache']);
80
	}
81
82
83
84
	public function loadConfiguration()
85
	{
86
		$this->loaders = [];
87
88
		$builder = $this->getContainerBuilder();
89
		$config = $this->getConfig();
90
91
		$translator = $builder->addDefinition($this->prefix('default'))
92
			->setClass('Kdyby\Translation\Translator', [$this->prefix('@userLocaleResolver')])
93
			->addSetup('?->setTranslator(?)', [$this->prefix('@userLocaleResolver.param'), '@self'])
94
			->addSetup('setDefaultLocale', [$config['default']])
95
			->addSetup('setLocaleWhitelist', [$config['whitelist']]);
96
97
		Validators::assertField($config, 'fallback', 'list');
98
		$translator->addSetup('setFallbackLocales', [$config['fallback']]);
99
100
		$catalogueCompiler = $builder->addDefinition($this->prefix('catalogueCompiler'))
101
			->setClass('Kdyby\Translation\CatalogueCompiler', self::filterArgs($config['cache']));
102
103
		if ($config['debugger'] && interface_exists('Tracy\IBarPanel')) {
104
			$builder->addDefinition($this->prefix('panel'))
105
				->setClass('Kdyby\Translation\Diagnostics\Panel', [dirname($builder->expand('%appDir%'))])
106
				->addSetup('setLocaleWhitelist', [$config['whitelist']]);
107
108
			$translator->addSetup('?->register(?)', [$this->prefix('@panel'), '@self']);
109
			$catalogueCompiler->addSetup('enableDebugMode');
110
		}
111
112
		$this->loadLocaleResolver($config);
113
114
		$builder->addDefinition($this->prefix('helpers'))
115
			->setClass('Kdyby\Translation\TemplateHelpers')
116
			->setFactory($this->prefix('@default') . '::createTemplateHelpers');
117
118
		$builder->addDefinition($this->prefix('fallbackResolver'))
119
			->setClass('Kdyby\Translation\FallbackResolver');
120
121
		$builder->addDefinition($this->prefix('catalogueFactory'))
122
			->setClass('Kdyby\Translation\CatalogueFactory');
123
124
		$builder->addDefinition($this->prefix('selector'))
125
			->setClass('Symfony\Component\Translation\MessageSelector');
126
127
		$builder->addDefinition($this->prefix('extractor'))
128
			->setClass('Symfony\Component\Translation\Extractor\ChainExtractor');
129
130
		$this->loadExtractors();
131
132
		$builder->addDefinition($this->prefix('writer'))
133
			->setClass('Symfony\Component\Translation\Writer\TranslationWriter');
134
135
		$this->loadDumpers();
136
137
		$builder->addDefinition($this->prefix('loader'))
138
			->setClass('Kdyby\Translation\TranslationLoader');
139
140
		$loaders = $this->loadFromFile(__DIR__ . '/config/loaders.neon');
141
		$this->loadLoaders($loaders, $config['loaders'] ? : array_keys($loaders));
142
143
		if ($this->isRegisteredConsoleExtension()) {
144
			$this->loadConsole($config);
145
		}
146
	}
147
148
149
150
	protected function loadLocaleResolver(array $config)
151
	{
152
		$builder = $this->getContainerBuilder();
153
154
		$builder->addDefinition($this->prefix('userLocaleResolver.param'))
155
			->setClass('Kdyby\Translation\LocaleResolver\LocaleParamResolver')
156
			->setAutowired(FALSE);
157
158
		$builder->addDefinition($this->prefix('userLocaleResolver.acceptHeader'))
159
			->setClass('Kdyby\Translation\LocaleResolver\AcceptHeaderResolver');
160
161
		$builder->addDefinition($this->prefix('userLocaleResolver.session'))
162
			->setClass('Kdyby\Translation\LocaleResolver\SessionResolver');
163
164
		$chain = $builder->addDefinition($this->prefix('userLocaleResolver'))
165
			->setClass('Kdyby\Translation\IUserLocaleResolver')
166
			->setFactory('Kdyby\Translation\LocaleResolver\ChainResolver');
167
168
		$resolvers = [];
169 View Code Duplication
		if ($config['resolvers'][self::RESOLVER_HEADER]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
			$resolvers[] = $this->prefix('@userLocaleResolver.acceptHeader');
171
			$chain->addSetup('addResolver', [$this->prefix('@userLocaleResolver.acceptHeader')]);
172
		}
173
174 View Code Duplication
		if ($config['resolvers'][self::RESOLVER_REQUEST]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
175
			$resolvers[] = $this->prefix('@userLocaleResolver.param');
176
			$chain->addSetup('addResolver', [$this->prefix('@userLocaleResolver.param')]);
177
		}
178
179 View Code Duplication
		if ($config['resolvers'][self::RESOLVER_SESSION]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
180
			$resolvers[] = $this->prefix('@userLocaleResolver.session');
181
			$chain->addSetup('addResolver', [$this->prefix('@userLocaleResolver.session')]);
182
		}
183
184
		if ($config['debugger'] && interface_exists('Tracy\IBarPanel')) {
185
			$builder->getDefinition($this->prefix('panel'))
186
				->addSetup('setLocaleResolvers', [array_reverse($resolvers)]);
187
		}
188
	}
189
190
191
192
	protected function loadConsole(array $config)
193
	{
194
		$builder = $this->getContainerBuilder();
195
196
		Validators::assertField($config, 'dirs', 'list');
197
		$builder->addDefinition($this->prefix('console.extract'))
198
			->setClass('Kdyby\Translation\Console\ExtractCommand')
199
			->addSetup('$defaultOutputDir', [reset($config['dirs'])])
200
			->addTag('kdyby.console.command', 'latte');
201
	}
202
203
204
205 View Code Duplication
	protected function loadDumpers()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
	{
207
		$builder = $this->getContainerBuilder();
208
209
		foreach ($this->loadFromFile(__DIR__ . '/config/dumpers.neon') as $format => $class) {
210
			$builder->addDefinition($this->prefix('dumper.' . $format))
211
				->setClass($class)
212
				->addTag(self::TAG_DUMPER, $format);
213
		}
214
	}
215
216
217
218
	protected function loadLoaders(array $loaders, array $allowed)
219
	{
220
		$builder = $this->getContainerBuilder();
221
222
		foreach ($loaders as $format => $class) {
223
			if (array_search($format, $allowed) === FALSE) {
224
				continue;
225
			}
226
			$builder->addDefinition($this->prefix('loader.' . $format))
227
				->setClass($class)
228
				->addTag(self::TAG_LOADER, $format);
229
		}
230
	}
231
232
233
234 View Code Duplication
	protected function loadExtractors()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
235
	{
236
		$builder = $this->getContainerBuilder();
237
238
		foreach ($this->loadFromFile(__DIR__ . '/config/extractors.neon') as $format => $class) {
239
			$builder->addDefinition($this->prefix('extractor.' . $format))
240
				->setClass($class)
241
				->addTag(self::TAG_EXTRACTOR, $format);
242
		}
243
	}
244
245
246
247
	public function beforeCompile()
248
	{
249
		$builder = $this->getContainerBuilder();
250
		$config = $this->getConfig();
251
252
		$this->beforeCompileLogging($config);
253
254
		$registerToLatte = function (Nette\DI\ServiceDefinition $def) {
255
			$def->addSetup('?->onCompile[] = function($engine) { Kdyby\Translation\Latte\TranslateMacros::install($engine->getCompiler()); }', ['@self']);
256
257
			$def->addSetup('addProvider', ['translator', $this->prefix('@default')])
258
				->addSetup('addFilter', ['translate', [$this->prefix('@helpers'), 'translateFilterAware']]);
259
		};
260
261
		$latteFactoryService = $builder->getByType('Nette\Bridges\ApplicationLatte\ILatteFactory');
262
		if (!$latteFactoryService || !self::isOfType($builder->getDefinition($latteFactoryService)->getClass(), 'Latte\engine')) {
263
			$latteFactoryService = 'nette.latteFactory';
264
		}
265
266
		if ($builder->hasDefinition($latteFactoryService) && self::isOfType($builder->getDefinition($latteFactoryService)->getClass(), 'Latte\Engine')) {
267
			$registerToLatte($builder->getDefinition($latteFactoryService));
268
		}
269
270
		if ($builder->hasDefinition('nette.latte')) {
271
			$registerToLatte($builder->getDefinition('nette.latte'));
272
		}
273
274
		$applicationService = $builder->getByType('Nette\Application\Application') ?: 'application';
275
		if ($builder->hasDefinition($applicationService)) {
276
			$builder->getDefinition($applicationService)
277
				->addSetup('$service->onRequest[] = ?', [[$this->prefix('@userLocaleResolver.param'), 'onRequest']]);
278
279
			if ($config['debugger'] && interface_exists('Tracy\IBarPanel')) {
280
				$builder->getDefinition($applicationService)
281
					->addSetup('$self = $this; $service->onStartup[] = function () use ($self) { $self->getService(?); }', [$this->prefix('default')])
282
					->addSetup('$service->onRequest[] = ?', [[$this->prefix('@panel'), 'onRequest']]);
283
			}
284
		}
285
286
		if (class_exists('Tracy\Debugger')) {
287
			Kdyby\Translation\Diagnostics\Panel::registerBluescreen();
288
		}
289
290
		$extractor = $builder->getDefinition($this->prefix('extractor'));
291 View Code Duplication
		foreach ($builder->findByTag(self::TAG_EXTRACTOR) as $extractorId => $meta) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
292
			Validators::assert($meta, 'string:2..');
293
294
			$extractor->addSetup('addExtractor', [$meta, '@' . $extractorId]);
295
296
			$builder->getDefinition($extractorId)->setAutowired(FALSE);
297
		}
298
299
		$writer = $builder->getDefinition($this->prefix('writer'));
300 View Code Duplication
		foreach ($builder->findByTag(self::TAG_DUMPER) as $dumperId => $meta) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
301
			Validators::assert($meta, 'string:2..');
302
303
			$writer->addSetup('addDumper', [$meta, '@' . $dumperId]);
304
305
			$builder->getDefinition($dumperId)->setAutowired(FALSE);
306
		}
307
308
		$this->loaders = [];
309
		foreach ($builder->findByTag(self::TAG_LOADER) as $loaderId => $meta) {
310
			Validators::assert($meta, 'string:2..');
311
			$builder->getDefinition($loaderId)->setAutowired(FALSE);
312
			$this->loaders[$meta] = $loaderId;
313
		}
314
315
		$builder->getDefinition($this->prefix('loader'))
316
			->addSetup('injectServiceIds', [$this->loaders]);
317
318
		foreach ($this->compiler->getExtensions() as $extension) {
319
			if (!$extension instanceof ITranslationProvider) {
320
				continue;
321
			}
322
323
			$config['dirs'] = array_merge($config['dirs'], array_values($extension->getTranslationResources()));
324
		}
325
326
		$config['dirs'] = array_map(function ($dir) {
327
			return str_replace((DIRECTORY_SEPARATOR === '/') ? '\\' : '/', DIRECTORY_SEPARATOR, $dir);
328
		}, $config['dirs']);
329
330
		if ($dirs = array_values(array_filter($config['dirs'], Callback::closure('is_dir')))) {
331
			foreach ($dirs as $dir) {
332
				$builder->addDependency($dir);
333
			}
334
335
			$this->loadResourcesFromDirs($dirs);
336
		}
337
	}
338
339
340
341
	protected function beforeCompileLogging(array $config)
342
	{
343
		$builder = $this->getContainerBuilder();
344
		$translator = $builder->getDefinition($this->prefix('default'));
345
346
		if ($config['logging'] === TRUE) {
347
			$translator->addSetup('injectPsrLogger');
348
349
		} elseif (is_string($config['logging'])) { // channel for kdyby/monolog
350
			$translator->addSetup('injectPsrLogger', [
351
				new Statement('@Kdyby\Monolog\Logger::channel', [$config['logging']]),
352
			]);
353
354
		} elseif ($config['logging'] !== NULL) {
355
			throw new Kdyby\Translation\InvalidArgumentException(sprintf(
356
				"Invalid config option for logger. Valid are TRUE for general psr/log or string for kdyby/monolog channel, but %s was given",
357
				$config['logging']
358
			));
359
		}
360
	}
361
362
363
364
	protected function loadResourcesFromDirs($dirs)
365
	{
366
		$builder = $this->getContainerBuilder();
367
		$config = $this->getConfig();
368
369
		$whitelistRegexp = Kdyby\Translation\Translator::buildWhitelistRegexp($config['whitelist']);
370
		$translator = $builder->getDefinition($this->prefix('default'));
371
372
		$mask = array_map(function ($value) {
373
			return '*.*.' . $value;
374
		}, array_keys($this->loaders));
375
376
		foreach (Finder::findFiles($mask)->from($dirs) as $file) {
377
			/** @var \SplFileInfo $file */
378
			if (!$m = Strings::match($file->getFilename(), '~^(?P<domain>.*?)\.(?P<locale>[^\.]+)\.(?P<format>[^\.]+)$~')) {
379
				continue;
380
			}
381
382
			if ($whitelistRegexp && !preg_match($whitelistRegexp, $m['locale']) && $builder->parameters['productionMode']) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $whitelistRegexp of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
383
				continue; // ignore in production mode, there is no need to pass the ignored resources
384
			}
385
386
			$this->validateResource($m['format'], $file->getPathname(), $m['locale'], $m['domain']);
387
			$translator->addSetup('addResource', [$m['format'], $file->getPathname(), $m['locale'], $m['domain']]);
388
			$builder->addDependency($file->getPathname());
389
		}
390
	}
391
392
393
394
	/**
395
	 * @param string $format
396
	 * @param string $file
397
	 * @param string $locale
398
	 * @param string $domain
399
	 */
400
	protected function validateResource($format, $file, $locale, $domain)
401
	{
402
		$builder = $this->getContainerBuilder();
403
404
		if (!isset($this->loaders[$format])) {
405
			return;
406
		}
407
408
		try {
409
			$def = $builder->getDefinition($this->loaders[$format]);
410
			$refl = Reflection\ClassType::from($def->getEntity() ?: $def->getClass());
411
			$method = $refl->getConstructor();
412
			if ($method !== null && $method->getNumberOfRequiredParameters() > 1) {
413
				return;
414
			}
415
416
			$loader = $refl->newInstance();
417
			if (!$loader instanceof LoaderInterface) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\Transl...\Loader\LoaderInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
418
				return;
419
			}
420
421
		} catch (\ReflectionException $e) {
422
			return;
423
		}
424
425
		try {
426
			$loader->load($file, $locale, $domain);
427
428
		} catch (\Exception $e) {
429
			throw new InvalidResourceException("Resource $file is not valid and cannot be loaded.", 0, $e);
430
		}
431
	}
432
433
434
435
	public function afterCompile(Code\ClassType $class)
436
	{
437
		$initialize = $class->getMethod('initialize');
438
		if (class_exists('Tracy\Debugger')) {
439
			$initialize->addBody('Kdyby\Translation\Diagnostics\Panel::registerBluescreen();');
440
		}
441
	}
442
443
444
445
	/**
446
	 * {@inheritdoc}
447
	 */
448
	public function getConfig(array $defaults = NULL, $expand = TRUE)
449
	{
450
		return parent::getConfig($this->defaults) + ['fallback' => ['en_US']];
451
	}
452
453
454
455
	private function isRegisteredConsoleExtension()
456
	{
457
		foreach ($this->compiler->getExtensions() as $extension) {
458
			if ($extension instanceof Kdyby\Console\DI\ConsoleExtension) {
0 ignored issues
show
Bug introduced by
The class Kdyby\Console\DI\ConsoleExtension does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
459
				return TRUE;
460
			}
461
		}
462
463
		return FALSE;
464
	}
465
466
467
468
	/**
469
	 * @param \Nette\Configurator $configurator
470
	 */
471
	public static function register(Nette\Configurator $configurator)
472
	{
473
		$configurator->onCompile[] = function ($config, Nette\DI\Compiler $compiler) {
474
			$compiler->addExtension('translation', new TranslationExtension());
475
		};
476
	}
477
478
479
480
	/**
481
	 * @param string|\stdClass $statement
482
	 * @return Nette\DI\Statement[]
483
	 */
484
	protected static function filterArgs($statement)
485
	{
486
		return \Nette\DI\Helpers::filterArguments([is_string($statement) ? new Nette\DI\Statement($statement) : $statement]);
487
	}
488
489
490
491
	/**
492
	 * @param string|NULL $class
493
	 * @param string $type
494
	 * @return bool
495
	 */
496
	private static function isOfType($class, $type)
497
	{
498
		return $class !== NULL && ($class === $type || is_subclass_of($class, $type));
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $type can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
499
	}
500
501
}
502