yii\console\controllers\AssetController provides dependency trace in case bundle circular dependency detected

This commit is contained in:
Paul Klimov
2015-04-03 19:22:42 +03:00
parent d8455835ee
commit 378937f8d5
3 changed files with 81 additions and 3 deletions

View File

@ -21,6 +21,7 @@ Yii Framework 2 Change Log
- Enh #6975: Pressing arrows while focused in inputs of Active Form with `validateOnType` enabled no longer triggers validation (slinstj) - Enh #6975: Pressing arrows while focused in inputs of Active Form with `validateOnType` enabled no longer triggers validation (slinstj)
- Enh #7488: Added `StringHelper::explode` to perform explode with trimming and skipping of empty elements (SilverFire, nineinchnick, creocoder, samdark) - Enh #7488: Added `StringHelper::explode` to perform explode with trimming and skipping of empty elements (SilverFire, nineinchnick, creocoder, samdark)
- Enh #7530: Improved default values for `yii\data\Sort` link labels in a `ListView` when used with an `ActiveDataProvider` (cebe) - Enh #7530: Improved default values for `yii\data\Sort` link labels in a `ListView` when used with an `ActiveDataProvider` (cebe)
- Enh #7539: `yii\console\controllers\AssetController` provides dependency trace in case bundle circular dependency detected (klimov-paul)
- Enh #7562: `yii help` now lists all sub-commands by default (callmez) - Enh #7562: `yii help` now lists all sub-commands by default (callmez)
- Enh #7571: HTTP status 500 and "An internal server error occurred." are now returned in case there was an exception in layout and `YII_DEBUG` is false (samdark) - Enh #7571: HTTP status 500 and "An internal server error occurred." are now returned in case there was an exception in layout and `YII_DEBUG` is false (samdark)
- Enh #7636: `yii\web\Session::getHasSessionId()` uses a more lenient way to check if session ID is provided in URL (robsch) - Enh #7636: `yii\web\Session::getHasSessionId()` uses a more lenient way to check if session ID is provided in URL (robsch)

View File

@ -248,7 +248,7 @@ class AssetController extends Controller
$this->loadDependency($dependencyBundle, $result); $this->loadDependency($dependencyBundle, $result);
$result[$name] = $dependencyBundle; $result[$name] = $dependencyBundle;
} elseif ($result[$name] === false) { } elseif ($result[$name] === false) {
throw new Exception("A circular dependency is detected for bundle '$name'."); throw new Exception("A circular dependency is detected for bundle '{$name}': " . $this->composeCircularDependencyTrace($name, $result) . ".");
} }
} }
} }
@ -423,9 +423,9 @@ class AssetController extends Controller
$this->registerBundle($bundles, $depend, $registered); $this->registerBundle($bundles, $depend, $registered);
} }
unset($registered[$name]); unset($registered[$name]);
$registered[$name] = true; $registered[$name] = $bundle;
} elseif ($registered[$name] === false) { } elseif ($registered[$name] === false) {
throw new Exception("A circular dependency is detected for target '$name'."); throw new Exception("A circular dependency is detected for target '{$name}': " . $this->composeCircularDependencyTrace($name, $registered) . ".");
} }
} }
@ -756,4 +756,26 @@ EOD;
$config['class'] = get_class($bundle); $config['class'] = get_class($bundle);
return $config; return $config;
} }
/**
* Composes trace info for bundle circular dependency.
* @param string $circularDependencyName name of the bundle, which have circular dependency
* @param array $registered list of bundles registered while detecting circular dependency.
* @return string bundle circular dependency trace string.
*/
private function composeCircularDependencyTrace($circularDependencyName, array $registered)
{
$dependencyTrace = [];
$startFound = false;
foreach ($registered as $name => $value) {
if ($name === $circularDependencyName) {
$startFound = true;
}
if ($startFound && $value === false) {
$dependencyTrace[] = $name;
}
}
$dependencyTrace[] = $circularDependencyName;
return implode(' -> ', $dependencyTrace);
}
} }

View File

@ -377,6 +377,61 @@ EOL;
$this->assertContains($externalAssetBundleClassName, $compressedRegularAssetConfig['depends'], 'Dependency on external bundle is lost!'); $this->assertContains($externalAssetBundleClassName, $compressedRegularAssetConfig['depends'], 'Dependency on external bundle is lost!');
} }
/**
* @depends testActionCompress
*
* @see https://github.com/yiisoft/yii2/issues/7539
*/
public function testDetectCircularDependency()
{
// Given :
$namespace = __NAMESPACE__;
$this->declareAssetBundleClass([
'namespace' => $namespace,
'class' => 'AssetStart',
'depends' => [
$namespace . '\AssetA'
],
]);
$this->declareAssetBundleClass([
'namespace' => $namespace,
'class' => 'AssetA',
'depends' => [
$namespace . '\AssetB'
],
]);
$this->declareAssetBundleClass([
'namespace' => $namespace,
'class' => 'AssetB',
'depends' => [
$namespace . '\AssetC'
],
]);
$this->declareAssetBundleClass([
'namespace' => $namespace,
'class' => 'AssetC',
'depends' => [
$namespace . '\AssetA'
],
]);
$bundles = [
$namespace . '\AssetStart'
];
$bundleFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'bundle.php';
$configFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'config.php';
$this->createCompressConfigFile($configFile, $bundles);
// Assert :
$expectedExceptionMessage = ": {$namespace}\AssetA -> {$namespace}\AssetB -> {$namespace}\AssetC -> {$namespace}\AssetA";
$this->setExpectedException('yii\console\Exception', $expectedExceptionMessage);
// When :
$this->runAssetControllerAction('compress', [$configFile, $bundleFile]);
}
/** /**
* Data provider for [[testAdjustCssUrl()]]. * Data provider for [[testAdjustCssUrl()]].
* @return array test data. * @return array test data.