Completed
Push — master ( 4e569b...f4448f )
by Filip
03:08
created

TranslationExtension::isOfType()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 2
eloc 2
nc 2
nop 2
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
	/** @deprecated */
35
	const LOADER_TAG = self::TAG_LOADER;
36
	/** @deprecated */
37
	const DUMPER_TAG = self::TAG_DUMPER;
38
	/** @deprecated */
39
	const EXTRACTOR_TAG = self::TAG_EXTRACTOR;
40
41
	const TAG_LOADER = 'translation.loader';
42
	const TAG_DUMPER = 'translation.dumper';
43
	const TAG_EXTRACTOR = 'translation.extractor';
44
45
	const RESOLVER_REQUEST = 'request';
46
	const RESOLVER_HEADER = 'header';
47
	const RESOLVER_SESSION = 'session';
48
49
	/**
50
	 * @var array
51
	 */
52
	public $defaults = [
53
		'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...
54
		'default' => 'en',
55
		'logging' => NULL, //  TRUE for psr/log, or string for kdyby/monolog channel
56
		// '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...
57
		'dirs' => ['%appDir%/lang', '%appDir%/locale'],
58
		'cache' => 'Kdyby\Translation\Caching\PhpFileStorage',
59
		'debugger' => '%debugMode%',
60
		'resolvers' => [
61
			self::RESOLVER_SESSION => FALSE,
62
			self::RESOLVER_REQUEST => TRUE,
63
			self::RESOLVER_HEADER => TRUE,
64
		],
65
		'loaders' => []
66
	];
67
68
	/**
69
	 * @var array
70
	 */
71
	private $loaders;
72
73
74
75
	public function __construct()
76
	{
77
		$this->defaults['cache'] = new Statement($this->defaults['cache'], ['%tempDir%/cache']);
78
	}
79
80
81
82
	public function loadConfiguration()
83
	{
84
		$this->loaders = [];
85
86
		$builder = $this->getContainerBuilder();
87
		$config = $this->getConfig();
88
89
		$translator = $builder->addDefinition($this->prefix('default'))
90
			->setClass('Kdyby\Translation\Translator', [$this->prefix('@userLocaleResolver')])
91
			->addSetup('?->setTranslator(?)', [$this->prefix('@userLocaleResolver.param'), '@self'])
92
			->addSetup('setDefaultLocale', [$config['default']])
93
			->addSetup('setLocaleWhitelist', [$config['whitelist']])
94
			->setInject(FALSE);
95
96
		Validators::assertField($config, 'fallback', 'list');
97
		$translator->addSetup('setFallbackLocales', [$config['fallback']]);
98
99
		$catalogueCompiler = $builder->addDefinition($this->prefix('catalogueCompiler'))
100
			->setClass('Kdyby\Translation\CatalogueCompiler', self::filterArgs($config['cache']))
101
			->setInject(FALSE);
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%'))])
0 ignored issues
show
Deprecated Code introduced by
The method Nette\DI\ContainerBuilder::expand() has been deprecated.

This method has been deprecated.

Loading history...
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
			->setInject(FALSE);
118
119
		$builder->addDefinition($this->prefix('fallbackResolver'))
120
			->setClass('Kdyby\Translation\FallbackResolver')
121
			->setInject(FALSE);
122
123
		$builder->addDefinition($this->prefix('catalogueFactory'))
124
			->setClass('Kdyby\Translation\CatalogueFactory')
125
			->setInject(FALSE);
126
127
		$builder->addDefinition($this->prefix('selector'))
128
			->setClass('Symfony\Component\Translation\MessageSelector')
129
			->setInject(FALSE);
130
131
		$builder->addDefinition($this->prefix('extractor'))
132
			->setClass('Symfony\Component\Translation\Extractor\ChainExtractor')
133
			->setInject(FALSE);
134
135
		$this->loadExtractors();
136
137
		$builder->addDefinition($this->prefix('writer'))
138
			->setClass('Symfony\Component\Translation\Writer\TranslationWriter')
139
			->setInject(FALSE);
140
141
		$this->loadDumpers();
142
143
		$builder->addDefinition($this->prefix('loader'))
144
			->setClass('Kdyby\Translation\TranslationLoader')
145
			->setInject(FALSE);
146
147
		$loaders = $this->loadFromFile(__DIR__ . '/config/loaders.neon');
148
		$this->loadLoaders($loaders, $config['loaders'] ? : array_keys($loaders));
0 ignored issues
show
Bug introduced by
It seems like $loaders defined by $this->loadFromFile(__DI...'/config/loaders.neon') on line 147 can also be of type string; however, Kdyby\Translation\DI\Tra...xtension::loadLoaders() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
149
150
		if ($this->isRegisteredConsoleExtension()) {
151
			$this->loadConsole($config);
152
		}
153
	}
154
155
156
157
	protected function loadLocaleResolver(array $config)
158
	{
159
		$builder = $this->getContainerBuilder();
160
161
		$builder->addDefinition($this->prefix('userLocaleResolver.param'))
162
			->setClass('Kdyby\Translation\LocaleResolver\LocaleParamResolver')
163
			->setAutowired(FALSE)
164
			->setInject(FALSE);
165
166
		$builder->addDefinition($this->prefix('userLocaleResolver.acceptHeader'))
167
			->setClass('Kdyby\Translation\LocaleResolver\AcceptHeaderResolver')
168
			->setInject(FALSE);
169
170
		$builder->addDefinition($this->prefix('userLocaleResolver.session'))
171
			->setClass('Kdyby\Translation\LocaleResolver\SessionResolver')
172
			->setInject(FALSE);
173
174
		$chain = $builder->addDefinition($this->prefix('userLocaleResolver'))
175
			->setClass('Kdyby\Translation\IUserLocaleResolver')
176
			->setFactory('Kdyby\Translation\LocaleResolver\ChainResolver')
177
			->setInject(FALSE);
178
179
		$resolvers = [];
180 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...
181
			$resolvers[] = $this->prefix('@userLocaleResolver.acceptHeader');
182
			$chain->addSetup('addResolver', [$this->prefix('@userLocaleResolver.acceptHeader')]);
183
		}
184
185 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...
186
			$resolvers[] = $this->prefix('@userLocaleResolver.param');
187
			$chain->addSetup('addResolver', [$this->prefix('@userLocaleResolver.param')]);
188
		}
189
190 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...
191
			$resolvers[] = $this->prefix('@userLocaleResolver.session');
192
			$chain->addSetup('addResolver', [$this->prefix('@userLocaleResolver.session')]);
193
		}
194
195
		if ($config['debugger'] && interface_exists('Tracy\IBarPanel')) {
196
			$builder->getDefinition($this->prefix('panel'))
197
				->addSetup('setLocaleResolvers', [array_reverse($resolvers)]);
198
		}
199
	}
200
201
202
203
	protected function loadConsole(array $config)
204
	{
205
		$builder = $this->getContainerBuilder();
206
207
		Validators::assertField($config, 'dirs', 'list');
208
		$builder->addDefinition($this->prefix('console.extract'))
209
			->setClass('Kdyby\Translation\Console\ExtractCommand')
210
			->addSetup('$defaultOutputDir', [reset($config['dirs'])])
211
			->setInject(FALSE)
212
			->addTag('kdyby.console.command', 'latte');
0 ignored issues
show
Documentation introduced by
'latte' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
213
	}
214
215
216
217 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...
218
	{
219
		$builder = $this->getContainerBuilder();
220
221
		foreach ($this->loadFromFile(__DIR__ . '/config/dumpers.neon') as $format => $class) {
0 ignored issues
show
Bug introduced by
The expression $this->loadFromFile(__DI...'/config/dumpers.neon') of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
222
			$builder->addDefinition($this->prefix('dumper.' . $format))
223
				->setClass($class)
224
				->addTag(self::TAG_DUMPER, $format);
0 ignored issues
show
Documentation introduced by
$format is of type integer|string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
225
		}
226
	}
227
228
229
230
	protected function loadLoaders(array $loaders, array $allowed)
231
	{
232
		$builder = $this->getContainerBuilder();
233
234
		foreach ($loaders as $format => $class) {
235
			if (array_search($format, $allowed) === FALSE) {
236
				continue;
237
			}
238
			$builder->addDefinition($this->prefix('loader.' . $format))
239
				->setClass($class)
240
				->addTag(self::TAG_LOADER, $format);
0 ignored issues
show
Documentation introduced by
$format is of type integer|string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
241
		}
242
	}
243
244
245
246 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...
247
	{
248
		$builder = $this->getContainerBuilder();
249
250
		foreach ($this->loadFromFile(__DIR__ . '/config/extractors.neon') as $format => $class) {
0 ignored issues
show
Bug introduced by
The expression $this->loadFromFile(__DI...onfig/extractors.neon') of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
251
			$builder->addDefinition($this->prefix('extractor.' . $format))
252
				->setClass($class)
253
				->addTag(self::TAG_EXTRACTOR, $format);
0 ignored issues
show
Documentation introduced by
$format is of type integer|string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
254
		}
255
	}
256
257
258
259
	public function beforeCompile()
260
	{
261
		$builder = $this->getContainerBuilder();
262
		$config = $this->getConfig();
263
264
		$this->beforeCompileLogging($config);
265
266
		$registerToLatte = function (Nette\DI\ServiceDefinition $def) {
267
			$def
268
				->addSetup('?->onCompile[] = function($engine) { Kdyby\Translation\Latte\TranslateMacros::install($engine->getCompiler()); }', ['@self'])
269
				->addSetup('addFilter', ['translate', [$this->prefix('@helpers'), 'translate']]);
270
271
			if (method_exists('Latte\Engine', 'addProvider')) { // Nette 2.4
272
				$def->addSetup('addProvider', ['translator', '@Kdyby\Translation\Translator']);
273
274
			} else {
275
				$def->addSetup('addFilter', ['getTranslator', [$this->prefix('@helpers'), 'getTranslator']]);
276
			}
277
		};
278
279
		$latteFactoryService = $builder->getByType('Nette\Bridges\ApplicationLatte\ILatteFactory');
280
		if (!$latteFactoryService || !self::isOfType($builder->getDefinition($latteFactoryService)->getClass(), 'Latte\engine')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $latteFactoryService of type string|null is loosely compared to false; 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...
281
			$latteFactoryService = 'nette.latteFactory';
282
		}
283
284
		if ($builder->hasDefinition($latteFactoryService) && self::isOfType($builder->getDefinition($latteFactoryService)->getClass(), 'Latte\Engine')) {
285
			$registerToLatte($builder->getDefinition($latteFactoryService));
286
		}
287
288
		if ($builder->hasDefinition('nette.latte')) {
289
			$registerToLatte($builder->getDefinition('nette.latte'));
290
		}
291
292
		$applicationService = $builder->getByType('Nette\Application\Application') ?: 'application';
293
		if ($builder->hasDefinition($applicationService)) {
294
			$builder->getDefinition($applicationService)
295
				->addSetup('$service->onRequest[] = ?', [[$this->prefix('@userLocaleResolver.param'), 'onRequest']]);
296
297
			if ($config['debugger'] && interface_exists('Tracy\IBarPanel')) {
298
				$builder->getDefinition($applicationService)
299
					->addSetup('$self = $this; $service->onStartup[] = function () use ($self) { $self->getService(?); }', [$this->prefix('default')])
300
					->addSetup('$service->onRequest[] = ?', [[$this->prefix('@panel'), 'onRequest']]);
301
			}
302
		}
303
304
		if (class_exists('Tracy\Debugger')) {
305
			Kdyby\Translation\Diagnostics\Panel::registerBluescreen();
306
		}
307
308
		$extractor = $builder->getDefinition($this->prefix('extractor'));
309 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...
310
			Validators::assert($meta, 'string:2..');
311
312
			$extractor->addSetup('addExtractor', [$meta, '@' . $extractorId]);
313
314
			$builder->getDefinition($extractorId)->setAutowired(FALSE)->setInject(FALSE);
315
		}
316
317
		$writer = $builder->getDefinition($this->prefix('writer'));
318 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...
319
			Validators::assert($meta, 'string:2..');
320
321
			$writer->addSetup('addDumper', [$meta, '@' . $dumperId]);
322
323
			$builder->getDefinition($dumperId)->setAutowired(FALSE)->setInject(FALSE);
324
		}
325
326
		$this->loaders = [];
327
		foreach ($builder->findByTag(self::TAG_LOADER) as $loaderId => $meta) {
328
			Validators::assert($meta, 'string:2..');
329
			$builder->getDefinition($loaderId)->setAutowired(FALSE)->setInject(FALSE);
330
			$this->loaders[$meta] = $loaderId;
331
		}
332
333
		$builder->getDefinition($this->prefix('loader'))
334
			->addSetup('injectServiceIds', [$this->loaders])
335
			->setInject(FALSE);
336
337
		foreach ($this->compiler->getExtensions() as $extension) {
338
			if (!$extension instanceof ITranslationProvider) {
339
				continue;
340
			}
341
342
			$config['dirs'] = array_merge($config['dirs'], array_values($extension->getTranslationResources()));
343
		}
344
345
		if ($dirs = array_values(array_filter($config['dirs'], Callback::closure('is_dir')))) {
346
			foreach ($dirs as $dir) {
347
				$builder->addDependency($dir);
348
			}
349
350
			$this->loadResourcesFromDirs($dirs);
351
		}
352
	}
353
354
355
356
	protected function beforeCompileLogging(array $config)
357
	{
358
		$builder = $this->getContainerBuilder();
359
		$translator = $builder->getDefinition($this->prefix('default'));
360
361
		if ($config['logging'] === TRUE) {
362
			$translator->addSetup('injectPsrLogger');
363
364
		} elseif (is_string($config['logging'])) { // channel for kdyby/monolog
365
			$translator->addSetup('injectPsrLogger', [
366
				new Statement('@Kdyby\Monolog\Logger::channel', [$config['logging']]),
367
			]);
368
369
		} elseif ($config['logging'] !== NULL) {
370
			throw new Kdyby\Translation\InvalidArgumentException(sprintf(
371
				"Invalid config option for logger. Valid are TRUE for general psr/log or string for kdyby/monolog channel, but %s was given",
372
				$config['logging']
373
			));
374
		}
375
	}
376
377
378
379
	protected function loadResourcesFromDirs($dirs)
380
	{
381
		$builder = $this->getContainerBuilder();
382
		$config = $this->getConfig();
383
384
		$whitelistRegexp = Kdyby\Translation\Translator::buildWhitelistRegexp($config['whitelist']);
385
		$translator = $builder->getDefinition($this->prefix('default'));
386
387
		$mask = array_map(function ($value) {
388
			return '*.*.' . $value;
389
		}, array_keys($this->loaders));
390
391
		foreach (Finder::findFiles($mask)->from($dirs) as $file) {
392
			/** @var \SplFileInfo $file */
393
			if (!$m = Strings::match($file->getFilename(), '~^(?P<domain>.*?)\.(?P<locale>[^\.]+)\.(?P<format>[^\.]+)$~')) {
394
				continue;
395
			}
396
397
			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...
398
				continue; // ignore in production mode, there is no need to pass the ignored resources
399
			}
400
401
			$this->validateResource($m['format'], $file->getPathname(), $m['locale'], $m['domain']);
402
			$translator->addSetup('addResource', [$m['format'], $file->getPathname(), $m['locale'], $m['domain']]);
403
			$builder->addDependency($file->getPathname());
404
		}
405
	}
406
407
408
409
	/**
410
	 * @param string $format
411
	 * @param string $file
412
	 * @param string $locale
413
	 * @param string $domain
414
	 */
415
	protected function validateResource($format, $file, $locale, $domain)
416
	{
417
		$builder = $this->getContainerBuilder();
418
419
		if (!isset($this->loaders[$format])) {
420
			return;
421
		}
422
423
		try {
424
			$def = $builder->getDefinition($this->loaders[$format]);
425
			$refl = Reflection\ClassType::from($def->getEntity() ?: $def->getClass());
426
			if (($method = $refl->getConstructor()) && $method->getNumberOfRequiredParameters() > 1) {
427
				return;
428
			}
429
430
			$loader = $refl->newInstance();
431
			if (!$loader instanceof LoaderInterface) {
432
				return;
433
			}
434
435
		} catch (\ReflectionException $e) {
436
			return;
437
		}
438
439
		try {
440
			$loader->load($file, $locale, $domain);
441
442
		} catch (\Exception $e) {
443
			throw new InvalidResourceException("Resource $file is not valid and cannot be loaded.", 0, $e);
444
		}
445
	}
446
447
448
449
	public function afterCompile(Code\ClassType $class)
450
	{
451
		$initialize = $class->getMethod('initialize');
452
		if (class_exists('Tracy\Debugger')) {
453
			$initialize->addBody('Kdyby\Translation\Diagnostics\Panel::registerBluescreen();');
454
		}
455
	}
456
457
458
459
	private function isRegisteredConsoleExtension()
460
	{
461
		foreach ($this->compiler->getExtensions() as $extension) {
462
			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...
463
				return TRUE;
464
			}
465
		}
466
467
		return FALSE;
468
	}
469
470
471
472
	/**
473
	 * {@inheritdoc}
474
	 */
475
	public function getConfig(array $defaults = NULL, $expand = TRUE)
0 ignored issues
show
Unused Code introduced by
The parameter $defaults is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $expand is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
476
	{
477
		return parent::getConfig($this->defaults) + ['fallback' => ['en_US']];
478
	}
479
480
481
482
	/**
483
	 * @param string|\stdClass $statement
484
	 * @return Nette\DI\Statement[]
485
	 */
486
	public static function filterArgs($statement)
487
	{
488
		return Nette\DI\Compiler::filterArguments([is_string($statement) ? new Nette\DI\Statement($statement) : $statement]);
0 ignored issues
show
Deprecated Code introduced by
The method Nette\DI\Compiler::filterArguments() has been deprecated.

This method has been deprecated.

Loading history...
489
	}
490
491
492
493
	/**
494
	 * @param \Nette\Configurator $configurator
495
	 */
496
	public static function register(Nette\Configurator $configurator)
497
	{
498
		$configurator->onCompile[] = function ($config, Nette\DI\Compiler $compiler) {
499
			$compiler->addExtension('translation', new TranslationExtension());
500
		};
501
	}
502
503
504
505
	/**
506
	 * @param string $class
507
	 * @param string $type
508
	 * @return bool
509
	 */
510
	private static function isOfType($class, $type)
511
	{
512
		return $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...
513
	}
514
515
}
516