Fixes #14449: Fix PHP 7.2 compatibility bugs and add explicit closure support in yii\base\Application

This commit is contained in:
Boudewijn Vahrmeijer
2017-07-14 11:23:03 +02:00
committed by Alexander Makarov
parent b0162d3a48
commit 03d53b785d
5 changed files with 73 additions and 11 deletions

View File

@ -58,12 +58,22 @@ If it is not accessed at all during a request, it will not be instantiated. Some
to instantiate an application component for every request, even if it is not explicitly accessed.
To do so, you may list its ID in the [[yii\base\Application::bootstrap|bootstrap]] property of the application.
You can also use Closures to bootstrap customized components. Returning a instantiated component is not
required. A Closure can also be used simply for running code after [[yii\base\Application]] instantiation.
For example, the following application configuration makes sure the `log` component is always loaded:
```php
[
'bootstrap' => [
'log',
function($app){
return new ComponentX();
},
function($app){
// some code
return;
}
],
'components' => [
'log' => [

View File

@ -3,7 +3,7 @@ Yii Framework 2 Change Log
2.0.13 under development
------------------------
- Bug #14449: Fix PHP 7.2 compatibility bugs and add explicit closure support in `yii\base\Application` (dynasource)
- Bug #7890: Allow `migrate/mark` to mark history at the point of the base migration (cebe)
- Bug #14206: `MySqlMutex`, `PgsqlMutex` and `OracleMutex` now use `useMaster()` to ensure lock is aquired on the same DB server (cebe, ryusoft)
- Chg #14321: `yii\widgets\MaskedInput` is now registering its JavaScript `clientOptions` initialization code in head section (DaveFerger)

View File

@ -168,6 +168,7 @@ abstract class Application extends Module
* - a module ID as specified via [[modules]].
* - a class name.
* - a configuration array.
* - a Closure
*
* During the bootstrapping process, each component will be instantiated. If the component class
* implements [[BootstrapInterface]], its [[BootstrapInterface::bootstrap()|bootstrap()]] method
@ -300,19 +301,25 @@ abstract class Application extends Module
}
}
foreach ($this->bootstrap as $class) {
foreach ($this->bootstrap as $mixed) {
$component = null;
if (is_string($class)) {
if ($this->has($class)) {
$component = $this->get($class);
} elseif ($this->hasModule($class)) {
$component = $this->getModule($class);
} elseif (strpos($class, '\\') === false) {
throw new InvalidConfigException("Unknown bootstrapping component ID: $class");
if ($mixed instanceof \Closure) {
Yii::trace('Bootstrap with Closure', __METHOD__);
if (!$component = call_user_func($mixed, $this)) {
continue;
}
} elseif (is_string($mixed)) {
if ($this->has($mixed)) {
$component = $this->get($mixed);
} elseif ($this->hasModule($mixed)) {
$component = $this->getModule($mixed);
} elseif (strpos($mixed, '\\') === false) {
throw new InvalidConfigException("Unknown bootstrapping component ID: $mixed");
}
}
if (!isset($component)) {
$component = Yii::createObject($class);
$component = Yii::createObject($mixed);
}
if ($component instanceof BootstrapInterface) {

View File

@ -1350,7 +1350,7 @@ class QueryBuilder extends \yii\base\Object
$values = (array) $values;
}
if ($column instanceof \Traversable || count($column) > 1) {
if ($column instanceof \Traversable || ((is_array($column) || $column instanceof \Countable) && count($column) > 1)) {
return $this->buildCompositeInCondition($operator, $column, $values, $params);
} elseif (is_array($column)) {
$column = reset($column);

View File

@ -8,6 +8,9 @@
namespace yiiunit\framework\base;
use Yii;
use yii\base\BootstrapInterface;
use yii\base\Component;
use yii\base\Module;
use yii\log\Dispatcher;
use yiiunit\TestCase;
@ -29,8 +32,50 @@ class ApplicationTest extends TestCase
$this->assertInstanceOf(DispatcherMock::className(), Yii::$app->log);
}
public function testBootstrap()
{
Yii::getLogger()->flush();
$this->mockApplication([
'components' => [
'withoutBootstrapInterface' => [
'class' => Component::class
],
'withBootstrapInterface' => [
'class' => BootstrapComponentMock::class
]
],
'modules' => [
'moduleX' => [
'class' => Module::class
]
],
'bootstrap' => [
'withoutBootstrapInterface',
'withBootstrapInterface',
'moduleX',
function () {
}
],
]);
$this->assertSame('Bootstrap with yii\base\Component', Yii::getLogger()->messages[0][0]);
$this->assertSame('Bootstrap with yiiunit\framework\base\BootstrapComponentMock::bootstrap()', Yii::getLogger()->messages[1][0]);
$this->assertSame('Loading module: moduleX', Yii::getLogger()->messages[2][0]);
$this->assertSame('Bootstrap with yii\base\Module', Yii::getLogger()->messages[3][0]);
$this->assertSame('Bootstrap with Closure', Yii::getLogger()->messages[4][0]);
}
}
class DispatcherMock extends Dispatcher
{
}
class BootstrapComponentMock extends Component implements BootstrapInterface
{
public function bootstrap($app)
{
}
}