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 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. 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: For example, the following application configuration makes sure the `log` component is always loaded:
```php ```php
[ [
'bootstrap' => [ 'bootstrap' => [
'log', 'log',
function($app){
return new ComponentX();
},
function($app){
// some code
return;
}
], ],
'components' => [ 'components' => [
'log' => [ 'log' => [

View File

@ -3,7 +3,7 @@ Yii Framework 2 Change Log
2.0.13 under development 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 #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) - 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) - 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 module ID as specified via [[modules]].
* - a class name. * - a class name.
* - a configuration array. * - a configuration array.
* - a Closure
* *
* During the bootstrapping process, each component will be instantiated. If the component class * During the bootstrapping process, each component will be instantiated. If the component class
* implements [[BootstrapInterface]], its [[BootstrapInterface::bootstrap()|bootstrap()]] method * 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; $component = null;
if (is_string($class)) { if ($mixed instanceof \Closure) {
if ($this->has($class)) { Yii::trace('Bootstrap with Closure', __METHOD__);
$component = $this->get($class); if (!$component = call_user_func($mixed, $this)) {
} elseif ($this->hasModule($class)) { continue;
$component = $this->getModule($class); }
} elseif (strpos($class, '\\') === false) { } elseif (is_string($mixed)) {
throw new InvalidConfigException("Unknown bootstrapping component ID: $class"); 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)) { if (!isset($component)) {
$component = Yii::createObject($class); $component = Yii::createObject($mixed);
} }
if ($component instanceof BootstrapInterface) { if ($component instanceof BootstrapInterface) {

View File

@ -1350,7 +1350,7 @@ class QueryBuilder extends \yii\base\Object
$values = (array) $values; $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); return $this->buildCompositeInCondition($operator, $column, $values, $params);
} elseif (is_array($column)) { } elseif (is_array($column)) {
$column = reset($column); $column = reset($column);

View File

@ -8,6 +8,9 @@
namespace yiiunit\framework\base; namespace yiiunit\framework\base;
use Yii; use Yii;
use yii\base\BootstrapInterface;
use yii\base\Component;
use yii\base\Module;
use yii\log\Dispatcher; use yii\log\Dispatcher;
use yiiunit\TestCase; use yiiunit\TestCase;
@ -29,8 +32,50 @@ class ApplicationTest extends TestCase
$this->assertInstanceOf(DispatcherMock::className(), Yii::$app->log); $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 DispatcherMock extends Dispatcher
{ {
} }
class BootstrapComponentMock extends Component implements BootstrapInterface
{
public function bootstrap($app)
{
}
}