Passed
Push — 4.4 ( c5d3f8...dad80f )
by Ingo
08:04
created

CoreKernel::getNestedFrom()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Core;
4
5
use InvalidArgumentException;
6
use Monolog\Handler\StreamHandler;
7
use Monolog\Logger;
8
use Psr\Log\LoggerInterface;
9
use SilverStripe\Config\Collections\CachedConfigCollection;
10
use SilverStripe\Control\Director;
11
use SilverStripe\Control\HTTPResponse;
12
use SilverStripe\Control\HTTPResponse_Exception;
13
use SilverStripe\Core\Cache\ManifestCacheFactory;
14
use SilverStripe\Core\Config\ConfigLoader;
15
use SilverStripe\Core\Config\CoreConfigFactory;
16
use SilverStripe\Core\Injector\Injector;
17
use SilverStripe\Core\Injector\InjectorLoader;
18
use SilverStripe\Core\Injector\SilverStripeServiceConfigurationLocator;
19
use SilverStripe\Core\Manifest\ClassLoader;
20
use SilverStripe\Core\Manifest\ClassManifest;
21
use SilverStripe\Core\Manifest\ModuleLoader;
22
use SilverStripe\Core\Manifest\ModuleManifest;
23
use SilverStripe\Dev\DebugView;
24
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
25
use SilverStripe\Logging\ErrorHandler;
26
use SilverStripe\ORM\DB;
27
use SilverStripe\View\PublicThemes;
28
use SilverStripe\View\SSViewer;
29
use SilverStripe\View\ThemeManifest;
30
use SilverStripe\View\ThemeResourceLoader;
31
use SilverStripe\Dev\Deprecation;
32
33
/**
34
 * Simple Kernel container
35
 */
36
class CoreKernel implements Kernel
37
{
38
    /**
39
     * @var Kernel
40
     */
41
    protected $nestedFrom = null;
42
43
    /**
44
     * @var Injector
45
     */
46
    protected $container = null;
47
48
    /**
49
     * @var string
50
     */
51
    protected $enviroment = null;
52
53
    /**
54
     * @var ClassLoader
55
     */
56
    protected $classLoader = null;
57
58
    /**
59
     * @var ModuleLoader
60
     */
61
    protected $moduleLoader = null;
62
63
    /**
64
     * @var ConfigLoader
65
     */
66
    protected $configLoader = null;
67
68
    /**
69
     * @var InjectorLoader
70
     */
71
    protected $injectorLoader = null;
72
73
    /**
74
     * @var ThemeResourceLoader
75
     */
76
    protected $themeResourceLoader = null;
77
78
    protected $basePath = null;
79
80
    /**
81
     * Indicates whether the Kernel has been booted already
82
     *
83
     * @var bool
84
     */
85
    private $booted = false;
86
87
    /**
88
     * Indicates whether the Kernel has been flushed on boot
89
     * Unitialized before boot
90
     *
91
     * @var bool
92
     */
93
    private $flush;
94
95
    /**
96
     * Create a new kernel for this application
97
     *
98
     * @param string $basePath Path to base dir for this application
99
     */
100
    public function __construct($basePath)
101
    {
102
        $this->basePath = $basePath;
103
104
        // Initialise the dependency injector as soon as possible, as it is
105
        // subsequently used by some of the following code
106
        $injectorLoader = InjectorLoader::inst();
107
        $injector = new Injector(array('locator' => SilverStripeServiceConfigurationLocator::class));
108
        $injectorLoader->pushManifest($injector);
109
        $this->setInjectorLoader($injectorLoader);
110
111
        // Manifest cache factory
112
        $manifestCacheFactory = $this->buildManifestCacheFactory();
113
114
        // Class loader
115
        $classLoader = ClassLoader::inst();
116
        $classLoader->pushManifest(new ClassManifest($basePath, $manifestCacheFactory));
117
        $this->setClassLoader($classLoader);
118
119
        // Module loader
120
        $moduleLoader = ModuleLoader::inst();
121
        $moduleManifest = new ModuleManifest($basePath, $manifestCacheFactory);
122
        $moduleLoader->pushManifest($moduleManifest);
123
        $this->setModuleLoader($moduleLoader);
124
125
        // Config loader
126
        // @todo refactor CoreConfigFactory
127
        $configFactory = new CoreConfigFactory($manifestCacheFactory);
128
        $configManifest = $configFactory->createRoot();
129
        $configLoader = ConfigLoader::inst();
130
        $configLoader->pushManifest($configManifest);
131
        $this->setConfigLoader($configLoader);
132
133
        // Load template manifest
134
        $themeResourceLoader = ThemeResourceLoader::inst();
135
        $themeResourceLoader->addSet(SSViewer::PUBLIC_THEME, new PublicThemes());
136
        $themeResourceLoader->addSet(SSViewer::DEFAULT_THEME, new ThemeManifest(
137
            $basePath,
138
            null, // project is defined in config, and this argument is deprecated
139
            $manifestCacheFactory
140
        ));
141
        $this->setThemeResourceLoader($themeResourceLoader);
142
    }
143
144
    /**
145
     * Get the environment type
146
     *
147
     * @return string
148
     *
149
     * @deprecated 5.0 use Director::get_environment_type() instead. Since 5.0 it should return only if kernel overrides. No checking SESSION or Environment.
150
     */
151
    public function getEnvironment()
152
    {
153
        // Check set
154
        if ($this->enviroment) {
155
            return $this->enviroment;
156
        }
157
158
        // Check saved session
159
        $env = $this->sessionEnvironment();
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Core\CoreKernel::sessionEnvironment() has been deprecated: 5.0 Use Director::get_session_environment_type() instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

159
        $env = /** @scrutinizer ignore-deprecated */ $this->sessionEnvironment();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
160
        if ($env) {
161
            return $env;
162
        }
163
164
        // Check getenv
165
        if ($env = Environment::getEnv('SS_ENVIRONMENT_TYPE')) {
166
            return $env;
167
        }
168
169
        return self::LIVE;
170
    }
171
172
    /**
173
     * Check or update any temporary environment specified in the session.
174
     *
175
     * @return null|string
176
     *
177
     * @deprecated 5.0 Use Director::get_session_environment_type() instead
178
     */
179
    protected function sessionEnvironment()
180
    {
181
        if (!$this->booted) {
182
            // session is not initialyzed yet, neither is manifest
183
            return null;
184
        }
185
186
        return Director::get_session_environment_type();
187
    }
188
189
    public function boot($flush = false)
190
    {
191
        $this->flush = $flush;
192
193
        $this->bootPHP();
194
        $this->bootManifests($flush);
195
        $this->bootErrorHandling();
196
        $this->bootDatabaseEnvVars();
197
        $this->bootConfigs();
198
        $this->bootDatabaseGlobals();
199
        $this->validateDatabase();
200
201
        $this->booted = true;
202
    }
203
204
    /**
205
     * Include all _config.php files
206
     */
207
    protected function bootConfigs()
208
    {
209
        global $project;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
210
        $projectBefore = $project;
211
        $config = ModuleManifest::config();
212
        // After loading all other app manifests, include _config.php files
213
        $this->getModuleLoader()->getManifest()->activateConfig();
214
        if ($project && $project !== $projectBefore) {
215
            Deprecation::notice('5.0', '$project global is deprecated');
216
            $config->set('project', $project);
217
        }
218
    }
219
220
    /**
221
     * Load default database configuration from the $database and $databaseConfig globals
222
     */
223
    protected function bootDatabaseGlobals()
224
    {
225
        // Now that configs have been loaded, we can check global for database config
226
        global $databaseConfig;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
227
        global $database;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
228
229
        // Case 1: $databaseConfig global exists. Merge $database in as needed
230
        if (!empty($databaseConfig)) {
231
            if (!empty($database)) {
232
                $databaseConfig['database'] =  $this->getDatabasePrefix() . $database . $this->getDatabaseSuffix();
233
            }
234
235
            // Only set it if its valid, otherwise ignore $databaseConfig entirely
236
            if (!empty($databaseConfig['database'])) {
237
                DB::setConfig($databaseConfig);
238
239
                return;
240
            }
241
        }
242
243
        // Case 2: $database merged into existing config
244
        if (!empty($database)) {
245
            $existing = DB::getConfig();
246
            $existing['database'] = $this->getDatabasePrefix() . $database . $this->getDatabaseSuffix();
247
248
            DB::setConfig($existing);
249
        }
250
    }
251
252
    /**
253
     * Load default database configuration from environment variable
254
     */
255
    protected function bootDatabaseEnvVars()
256
    {
257
        // Set default database config
258
        $databaseConfig = $this->getDatabaseConfig();
259
        $databaseConfig['database'] = $this->getDatabaseName();
260
        DB::setConfig($databaseConfig);
261
    }
262
263
    /**
264
     * Check that the database configuration is valid, throwing an HTTPResponse_Exception if it's not
265
     *
266
     * @throws HTTPResponse_Exception
267
     */
268
    protected function validateDatabase()
269
    {
270
        $databaseConfig = DB::getConfig();
271
        // Gracefully fail if no DB is configured
272
        if (empty($databaseConfig['database'])) {
273
            $this->detectLegacyEnvironment();
274
            $this->redirectToInstaller();
275
        }
276
    }
277
278
    /**
279
     * Check if there's a legacy _ss_environment.php file
280
     *
281
     * @throws HTTPResponse_Exception
282
     */
283
    protected function detectLegacyEnvironment()
284
    {
285
        // Is there an _ss_environment.php file?
286
        if (!file_exists($this->basePath . '/_ss_environment.php') &&
287
            !file_exists(dirname($this->basePath) . '/_ss_environment.php')
288
        ) {
289
            return;
290
        }
291
292
        // Build error response
293
        $dv = new DebugView();
294
        $body =
295
            $dv->renderHeader() .
296
            $dv->renderInfo(
297
                "Configuration Error",
298
                Director::absoluteBaseURL()
299
            ) .
300
            $dv->renderParagraph(
301
                'You need to replace your _ss_environment.php file with a .env file, or with environment variables.<br><br>'
302
                . 'See the <a href="https://docs.silverstripe.org/en/4/getting_started/environment_management/">'
303
                . 'Environment Management</a> docs for more information.'
304
            ) .
305
            $dv->renderFooter();
306
307
        // Raise error
308
        $response = new HTTPResponse($body, 500);
309
        throw new HTTPResponse_Exception($response);
310
    }
311
312
    /**
313
     * If missing configuration, redirect to install.php
314
     */
315
    protected function redirectToInstaller()
316
    {
317
        // Error if installer not available
318
        if (!file_exists(Director::publicFolder() . '/install.php')) {
319
            throw new HTTPResponse_Exception(
320
                'SilverStripe Framework requires database configuration defined via .env',
321
                500
322
            );
323
        }
324
325
        // Redirect to installer
326
        $response = new HTTPResponse();
327
        $response->redirect(Director::absoluteURL('install.php'));
328
        throw new HTTPResponse_Exception($response);
329
    }
330
331
    /**
332
     * Load database config from environment
333
     *
334
     * @return array
335
     */
336
    protected function getDatabaseConfig()
337
    {
338
        /** @skipUpgrade */
339
        $databaseConfig = [
340
            "type" => Environment::getEnv('SS_DATABASE_CLASS') ?: 'MySQLDatabase',
341
            "server" => Environment::getEnv('SS_DATABASE_SERVER') ?: 'localhost',
342
            "username" => Environment::getEnv('SS_DATABASE_USERNAME') ?: null,
343
            "password" => Environment::getEnv('SS_DATABASE_PASSWORD') ?: null,
344
        ];
345
346
        // Set the port if called for
347
        $dbPort = Environment::getEnv('SS_DATABASE_PORT');
348
        if ($dbPort) {
349
            $databaseConfig['port'] = $dbPort;
350
        }
351
352
        // Set the timezone if called for
353
        $dbTZ = Environment::getEnv('SS_DATABASE_TIMEZONE');
354
        if ($dbTZ) {
355
            $databaseConfig['timezone'] = $dbTZ;
356
        }
357
358
        // For schema enabled drivers:
359
        $dbSchema = Environment::getEnv('SS_DATABASE_SCHEMA');
360
        if ($dbSchema) {
361
            $databaseConfig["schema"] = $dbSchema;
362
        }
363
364
        // For SQlite3 memory databases (mainly for testing purposes)
365
        $dbMemory = Environment::getEnv('SS_DATABASE_MEMORY');
366
        if ($dbMemory) {
367
            $databaseConfig["memory"] = $dbMemory;
368
        }
369
370
        // Allow database adapters to handle their own configuration
371
        DatabaseAdapterRegistry::autoconfigure($databaseConfig);
372
        return $databaseConfig;
373
    }
374
375
    /**
376
     * @return string
377
     */
378
    protected function getDatabasePrefix()
379
    {
380
        return Environment::getEnv('SS_DATABASE_PREFIX') ?: '';
381
    }
382
383
    /**
384
     * @return string
385
     */
386
    protected function getDatabaseSuffix()
387
    {
388
        return Environment::getEnv('SS_DATABASE_SUFFIX') ?: '';
389
    }
390
391
    /**
392
     * Get name of database
393
     *
394
     * @return string
395
     */
396
    protected function getDatabaseName()
397
    {
398
        // Check globals
399
        global $database;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
400
401
        if (!empty($database)) {
402
            return $this->getDatabasePrefix() . $database . $this->getDatabaseSuffix();
403
        }
404
405
        global $databaseConfig;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
406
407
        if (!empty($databaseConfig['database'])) {
408
            return $databaseConfig['database']; // Note: Already includes prefix
409
        }
410
411
        // Check environment
412
        $database = Environment::getEnv('SS_DATABASE_NAME');
413
414
        if ($database) {
415
            return $this->getDatabasePrefix() . $database . $this->getDatabaseSuffix();
416
        }
417
418
        // Auto-detect name
419
        $chooseName = Environment::getEnv('SS_DATABASE_CHOOSE_NAME');
420
421
        if ($chooseName) {
422
            // Find directory to build name from
423
            $loopCount = (int)$chooseName;
424
            $databaseDir = $this->basePath;
425
            for ($i = 0; $i < $loopCount-1; $i++) {
426
                $databaseDir = dirname($databaseDir);
427
            }
428
429
            // Build name
430
            $database = str_replace('.', '', basename($databaseDir));
431
            $prefix = $this->getDatabasePrefix();
432
433
            if ($prefix) {
434
                $prefix = 'SS_';
435
            } else {
436
                // If no prefix, hard-code prefix into database global
437
                $prefix = '';
438
                $database = 'SS_' . $database;
439
            }
440
441
            return $prefix . $database;
442
        }
443
444
        // no DB name (may be optional for some connectors)
445
        return null;
446
    }
447
448
    /**
449
     * Initialise PHP with default variables
450
     */
451
    protected function bootPHP()
452
    {
453
        if ($this->getEnvironment() === self::LIVE) {
0 ignored issues
show
Deprecated Code introduced by
The function SilverStripe\Core\CoreKernel::getEnvironment() has been deprecated: 5.0 use Director::get_environment_type() instead. Since 5.0 it should return only if kernel overrides. No checking SESSION or Environment. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

453
        if (/** @scrutinizer ignore-deprecated */ $this->getEnvironment() === self::LIVE) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
454
            // limited to fatal errors and warnings in live mode
455
            error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE));
456
        } else {
457
            // Report all errors in dev / test mode
458
            error_reporting(E_ALL | E_STRICT);
459
        }
460
461
        /**
462
         * Ensure we have enough memory
463
         */
464
        Environment::increaseMemoryLimitTo('64M');
465
466
        // Ensure we don't run into xdebug's fairly conservative infinite recursion protection limit
467
        if (function_exists('xdebug_enable')) {
468
            $current = ini_get('xdebug.max_nesting_level');
469
            if ((int)$current < 200) {
470
                ini_set('xdebug.max_nesting_level', 200);
471
            }
472
        }
473
474
        /**
475
         * Set default encoding
476
         */
477
        mb_http_output('UTF-8');
478
        mb_internal_encoding('UTF-8');
479
        mb_regex_encoding('UTF-8');
480
481
        /**
482
         * Enable better garbage collection
483
         */
484
        gc_enable();
485
    }
486
487
    /**
488
     * @return ManifestCacheFactory
489
     */
490
    protected function buildManifestCacheFactory()
491
    {
492
        return new ManifestCacheFactory([
493
            'namespace' => 'manifestcache',
494
            'directory' => TempFolder::getTempFolder($this->basePath),
495
        ]);
496
    }
497
498
    /**
499
     * @return bool
500
     */
501
    protected function getIncludeTests()
502
    {
503
        return false;
504
    }
505
506
    /**
507
     * Boot all manifests
508
     *
509
     * @param bool $flush
510
     */
511
    protected function bootManifests($flush)
512
    {
513
        // Setup autoloader
514
        $this->getClassLoader()->init($this->getIncludeTests(), $flush);
515
516
        // Find modules
517
        $this->getModuleLoader()->init($this->getIncludeTests(), $flush);
518
519
        // Flush config
520
        if ($flush) {
521
            $config = $this->getConfigLoader()->getManifest();
522
            if ($config instanceof CachedConfigCollection) {
523
                $config->setFlush(true);
524
            }
525
        }
526
        // tell modules to sort, now that config is available
527
        $this->getModuleLoader()->getManifest()->sort();
528
529
        // Find default templates
530
        $defaultSet = $this->getThemeResourceLoader()->getSet('$default');
531
        if ($defaultSet instanceof ThemeManifest) {
532
            $defaultSet->setProject(
533
                ModuleManifest::config()->get('project')
534
            );
535
            $defaultSet->init($this->getIncludeTests(), $flush);
536
        }
537
    }
538
539
    /**
540
     * Turn on error handling
541
     */
542
    protected function bootErrorHandling()
543
    {
544
        // Register error handler
545
        $errorHandler = Injector::inst()->get(ErrorHandler::class);
546
        $errorHandler->start();
547
548
        // Register error log file
549
        $errorLog = Environment::getEnv('SS_ERROR_LOG');
550
        if ($errorLog) {
551
            $logger = Injector::inst()->get(LoggerInterface::class);
552
            if ($logger instanceof Logger) {
553
                $logger->pushHandler(new StreamHandler($this->basePath . '/' . $errorLog, Logger::WARNING));
554
            } else {
555
                user_error("SS_ERROR_LOG setting only works with Monolog, you are using another logger", E_USER_WARNING);
556
            }
557
        }
558
    }
559
560
    public function shutdown()
561
    {
562
    }
563
564
    public function nest()
565
    {
566
        // Clone this kernel, nesting config / injector manifest containers
567
        $kernel = clone $this;
568
        $kernel->setConfigLoader($this->configLoader->nest());
569
        $kernel->setInjectorLoader($this->injectorLoader->nest());
570
        $kernel->nestedFrom = $this;
571
        return $kernel;
572
    }
573
574
    public function activate()
575
    {
576
        $this->configLoader->activate();
577
        $this->injectorLoader->activate();
578
579
        // Self register
580
        $this->getInjectorLoader()
581
            ->getManifest()
582
            ->registerService($this, Kernel::class);
583
        return $this;
584
    }
585
586
    public function getNestedFrom()
587
    {
588
        return $this->nestedFrom;
589
    }
590
591
    public function getContainer()
592
    {
593
        return $this->getInjectorLoader()->getManifest();
594
    }
595
596
    public function setInjectorLoader(InjectorLoader $injectorLoader)
597
    {
598
        $this->injectorLoader = $injectorLoader;
599
        $injectorLoader
600
            ->getManifest()
601
            ->registerService($this, Kernel::class);
602
        return $this;
603
    }
604
605
    public function getInjectorLoader()
606
    {
607
        return $this->injectorLoader;
608
    }
609
610
    public function getClassLoader()
611
    {
612
        return $this->classLoader;
613
    }
614
615
    public function setClassLoader(ClassLoader $classLoader)
616
    {
617
        $this->classLoader = $classLoader;
618
        return $this;
619
    }
620
621
    public function getModuleLoader()
622
    {
623
        return $this->moduleLoader;
624
    }
625
626
    public function setModuleLoader(ModuleLoader $moduleLoader)
627
    {
628
        $this->moduleLoader = $moduleLoader;
629
        return $this;
630
    }
631
632
    public function setEnvironment($environment)
633
    {
634
        if (!in_array($environment, [self::DEV, self::TEST, self::LIVE, null])) {
635
            throw new InvalidArgumentException(
636
                "Director::set_environment_type passed '$environment'.  It should be passed dev, test, or live"
637
            );
638
        }
639
        $this->enviroment = $environment;
640
        return $this;
641
    }
642
643
    public function getConfigLoader()
644
    {
645
        return $this->configLoader;
646
    }
647
648
    public function setConfigLoader($configLoader)
649
    {
650
        $this->configLoader = $configLoader;
651
        return $this;
652
    }
653
654
    public function getThemeResourceLoader()
655
    {
656
        return $this->themeResourceLoader;
657
    }
658
659
    public function setThemeResourceLoader($themeResourceLoader)
660
    {
661
        $this->themeResourceLoader = $themeResourceLoader;
662
        return $this;
663
    }
664
665
    /**
666
     * Returns whether the Kernel has been flushed on boot
667
     *
668
     * @return bool|null null if the kernel hasn't been booted yet
669
     */
670
    public function isFlushed()
671
    {
672
        return $this->flush;
673
    }
674
}
675