Passed
Push — master ( ddbf8b...086098 )
by Robbie
11:17
created

Installer   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 661
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 352
dl 0
loc 661
rs 4.5599
c 0
b 0
f 0
wmc 58

How to fix   Complexity   

Complex Class

Complex classes like Installer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Installer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\Dev\Install;
4
5
use Exception;
6
use SilverStripe\Control\Cookie;
7
use SilverStripe\Control\HTTPApplication;
8
use SilverStripe\Control\HTTPRequest;
9
use SilverStripe\Control\HTTPRequestBuilder;
10
use SilverStripe\Core\Convert;
11
use SilverStripe\Core\CoreKernel;
12
use SilverStripe\Core\EnvironmentLoader;
13
use SilverStripe\Core\Kernel;
14
use SilverStripe\Core\Path;
15
use SilverStripe\ORM\DatabaseAdmin;
16
use SilverStripe\Security\DefaultAdminService;
17
use SilverStripe\Security\Security;
18
use SilverStripe\Control\Middleware\URLSpecialsMiddleware\SessionEnvTypeSwitcher;
19
20
/**
21
 * This installer doesn't use any of the fancy SilverStripe stuff in case it's unsupported.
22
 */
23
class Installer
24
{
25
    use InstallEnvironmentAware;
26
    use SessionEnvTypeSwitcher;
27
28
    /**
29
     * Errors during install
30
     *
31
     * @var array
32
     */
33
    protected $errors = [];
34
35
    /**
36
     * value='' attribute placeholder for password fields
37
     */
38
    const PASSWORD_PLACEHOLDER = '********';
39
40
    public function __construct($basePath = null)
41
    {
42
        $this->initBaseDir($basePath);
43
    }
44
45
    /**
46
     * Installer error
47
     *
48
     * @param string $message
49
     */
50
    protected function error($message = null)
51
    {
52
        $this->errors[] = $message;
53
    }
54
55
    protected function installHeader()
56
    {
57
        $clientPath = RESOURCES_DIR . (PUBLIC_DIR
58
            ? '/vendor/silverstripe/framework/src/Dev/Install/client'
59
            : '/silverstripe/framework/src/Dev/Install/client');
60
        ?>
61
        <html>
62
        <head>
63
            <meta charset="utf-8"/>
64
            <title>Installing SilverStripe...</title>
65
            <link rel="stylesheet" type="text/css" href="<?=$clientPath; ?>/styles/install.css"/>
66
            <script src="//code.jquery.com/jquery-1.7.2.min.js"></script>
67
        </head>
68
        <body>
69
        <div class="install-header">
70
            <div class="inner">
71
                <div class="brand">
72
                    <h1>SilverStripe</h1>
73
                </div>
74
            </div>
75
        </div>
76
77
        <div id="Navigation">&nbsp;</div>
78
        <div class="clear"><!-- --></div>
79
80
        <div class="main">
81
            <div class="inner">
82
                <h2>Installing SilverStripe...</h2>
83
84
                <p>I am now running through the installation steps (this should take about 30 seconds)</p>
85
86
                <p>If you receive a fatal error, refresh this page to continue the installation</p>
87
                <ul>
88
        <?php
89
    }
90
91
    public function install($config)
92
    {
93
        // Render header
94
        $this->installHeader();
95
        $isIIS = $this->isIIS();
96
        $isApache = $this->isApache();
97
        $projectDir = $this->getProjectDir();
98
        $projectSrcDir = $this->getProjectSrcDir();
99
100
        flush();
101
102
        // Send install stats
103
        if (!empty($config['stats'])) {
104
            $this->sendInstallStats($config);
105
        }
106
107
        // Cleanup _config.php
108
        $basePath = $this->getBaseDir();
109
        $appConfigPath = $basePath . "{$projectDir}/_config.php";
110
        if (file_exists($appConfigPath)) {
111
            // Truncate the contents of _config instead of deleting it - we can't re-create it because Windows handles
112
            // permissions slightly differently to UNIX based filesystems - it takes the permissions from the parent
113
            // directory instead of retaining them
114
            $fh = fopen($appConfigPath, 'wb');
115
            fclose($fh);
116
        }
117
118
        // Write all files
119
        $this->writeIndexPHP();
120
        $this->writeConfigPHP($config);
121
        $this->writeConfigYaml($config);
122
        $this->writeConfigEnv($config);
123
124
        // Write other stuff
125
        if (!$this->checkModuleExists('cms')) {
126
            $rootURLControllerPath = $basePath . "{$projectSrcDir}/RootURLController.php";
127
            $this->writeToFile($rootURLControllerPath, <<<PHP
128
<?php
129
130
use SilverStripe\\Control\\Controller;
131
132
class RootURLController extends Controller
133
{
134
    public function index()
135
    {
136
        echo "<html>Your site is now set up. Start adding controllers to app/src to get started.</html>";
137
    }
138
}
139
PHP
140
            );
141
        }
142
143
        // Write the appropriate web server configuration file for rewriting support
144
        if ($this->hasRewritingCapability()) {
145
            if ($isApache) {
146
                $this->createHtaccess();
147
            } elseif ($isIIS) {
148
                $this->createWebConfig();
149
            }
150
        }
151
152
        // Build request
153
        $request = HTTPRequestBuilder::createFromEnvironment();
154
155
        // Install kernel (fix to dev)
156
        $kernel = new CoreKernel(Path::normalise($basePath));
157
        $kernel->setEnvironment(Kernel::DEV);
158
        $app = new HTTPApplication($kernel);
159
160
        // Build db within HTTPApplication
161
        $app->execute($request, function (HTTPRequest $request) use ($config) {
162
            // Suppress cookie errors on install
163
            Cookie::config()->set('report_errors', false);
164
165
            // Start session and execute
166
            $request->getSession()->init($request);
167
168
            // Output status
169
            $this->statusMessage("Building database schema...");
170
171
            // Setup DB
172
            $dbAdmin = new DatabaseAdmin();
173
            $dbAdmin->setRequest($request);
174
            $dbAdmin->pushCurrent();
175
            $dbAdmin->doInit();
176
            $dbAdmin->doBuild(true);
177
178
            // Create default administrator user and group in database
179
            // (not using Security::setDefaultAdmin())
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% 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...
180
            $username = $config['admin']['username'];
181
            $password = $config['admin']['password'];
182
            $adminMember = DefaultAdminService::singleton()
183
                ->findOrCreateAdmin(
184
                    $username,
185
                    _t('SilverStripe\\Security\\DefaultAdminService.DefaultAdminFirstname', 'Default Admin')
186
                );
187
            $adminMember->Email = $username;
188
            $adminMember->Password = $password;
189
            $adminMember->PasswordEncryption = Security::config()->get('encryption_algorithm');
190
191
            try {
192
                $this->statusMessage('Creating default CMS admin account...');
193
                $adminMember->write();
194
            } catch (Exception $e) {
195
                $this->statusMessage(
196
                    sprintf('Warning: Default CMS admin account could not be created (error: %s)', $e->getMessage())
197
                );
198
            }
199
200
            $request->getSession()->set('username', $username);
201
            $request->getSession()->set('password', $password);
202
            $request->getSession()->save($request);
203
        }, true);
204
205
        // Check result of install
206
        if (!$this->errors) {
207
            // switch the session to Dev mode so that
208
            // flush does not require authentication
209
            // for the first time after installation
210
            $request['isDev'] = '1';
211
            $this->setSessionEnvType($request);
212
            unset($request['isDev']);
213
            $request->getSession()->save($request);
214
215
            if (isset($_SERVER['HTTP_HOST']) && $this->hasRewritingCapability()) {
216
                $this->statusMessage("Checking that friendly URLs work...");
217
                $this->checkRewrite();
218
            } else {
219
                $params = http_build_query($request->getVars() + ['flush' => '']);
220
221
                $destinationURL = 'index.php/' .
222
                    ($this->checkModuleExists('cms') ? "home/successfullyinstalled?$params" : "?$params");
223
224
                echo <<<HTML
225
                <li>SilverStripe successfully installed; I am now redirecting you to your SilverStripe site...</li>
226
                <script>
227
                    setTimeout(function() {
228
                        window.location = "$destinationURL";
229
                    }, 2000);
230
                </script>
231
                <noscript>
232
                <li><a href="$destinationURL">Click here to access your site.</a></li>
233
                </noscript>
234
HTML;
235
            }
236
        } else {
237
            // Output all errors
238
            $this->statusMessage('Encountered ' . count($this->errors) . ' errors during install:');
239
            echo "<ul>";
240
            foreach ($this->errors as $error) {
241
                $this->statusMessage($error);
242
            }
243
            echo "</ul>";
244
            $this->statusMessage('Please <a href="install.php">Click here</a> to return to the installer.');
245
        }
246
247
        return $this->errors;
248
    }
249
250
    protected function writeIndexPHP()
251
    {
252
        $content = <<<'PHP'
253
<?php
254
255
use SilverStripe\Control\HTTPApplication;
256
use SilverStripe\Control\HTTPRequestBuilder;
257
use SilverStripe\Core\CoreKernel;
258
259
// Find autoload.php
260
if (file_exists(__DIR__ . '/vendor/autoload.php')) {
261
    require __DIR__ . '/vendor/autoload.php';
262
} elseif (file_exists(__DIR__ . '/../vendor/autoload.php')) {
263
    require __DIR__ . '/../vendor/autoload.php';
264
} else {
265
    echo "autoload.php not found";
266
    die;
267
}
268
269
// Build request and detect flush
270
$request = HTTPRequestBuilder::createFromEnvironment();
271
272
// Default application
273
$kernel = new CoreKernel(BASE_PATH);
274
$app = new HTTPApplication($kernel);
275
$response = $app->handle($request);
276
$response->output();
277
PHP;
278
        $path = $this->getPublicDir() . 'index.php';
279
        $this->writeToFile($path, $content, true);
280
    }
281
282
    /**
283
     * Write all .env files
284
     *
285
     * @param $config
286
     */
287
    protected function writeConfigEnv($config)
288
    {
289
        if (!$config['usingEnv']) {
290
            return;
291
        }
292
293
        $path = $this->getBaseDir() . '.env';
294
        $vars = [];
295
296
        // Retain existing vars
297
        $env = new EnvironmentLoader();
298
        if (file_exists($path)) {
299
            $vars = $env->loadFile($path) ?: [];
300
        }
301
302
        // Set base URL
303
        if (!isset($vars['SS_BASE_URL']) && isset($_SERVER['HTTP_HOST'])) {
304
            $vars['SS_BASE_URL'] = 'http://' . $_SERVER['HTTP_HOST'] . BASE_URL;
305
        }
306
307
        // Set DB env
308
        if (empty($config['db']['database'])) {
309
            $vars['SS_DATABASE_CHOOSE_NAME'] = true;
310
        } else {
311
            $vars['SS_DATABASE_NAME'] = $config['db']['database'];
312
        }
313
        $vars['SS_DATABASE_CLASS'] = $config['db']['type'];
314
        if (isset($config['db']['server'])) {
315
            $vars['SS_DATABASE_SERVER'] = $config['db']['server'];
316
        }
317
        if (isset($config['db']['username'])) {
318
            $vars['SS_DATABASE_USERNAME'] = $config['db']['username'];
319
        }
320
        if (isset($config['db']['password'])) {
321
            $vars['SS_DATABASE_PASSWORD'] = $config['db']['password'];
322
        }
323
        if (isset($config['db']['path'])) {
324
            $vars['SS_DATABASE_PATH'] = $config['db']['path'];
325
            // sqlite compat
326
            $vars['SS_SQLITE_DATABASE_PATH'] = $config['db']['path'];
327
        }
328
        if (isset($config['db']['key'])) {
329
            $vars['SS_DATABASE_KEY'] = $config['db']['key'];
330
            // sqlite compat
331
            $vars['SS_SQLITE_DATABASE_KEY'] = $config['db']['key'];
332
        }
333
334
        // Write all env vars
335
        $lines = [
336
            '# Generated by SilverStripe Installer'
337
        ];
338
        ksort($vars);
339
        foreach ($vars as $key => $value) {
340
            $lines[] = $key . '="' . addcslashes($value, '"') . '"';
341
        }
342
343
        $this->writeToFile('.env', implode("\n", $lines));
344
345
        // Re-load env vars for installer access
346
        $env->loadFile($path);
347
    }
348
349
    /**
350
     * Write all *.php files
351
     *
352
     * @param array $config
353
     */
354
    protected function writeConfigPHP($config)
355
    {
356
        $configPath = $this->getProjectDir() . DIRECTORY_SEPARATOR . "_config.php";
357
        if ($config['usingEnv']) {
358
            $this->writeToFile($configPath, "<?php\n ");
359
            return;
360
        }
361
362
        // Create databaseConfig
363
        $lines = [];
364
        foreach ($config['db'] as $key => $value) {
365
            $lines[] = sprintf(
366
                "    '%s' => '%s'",
367
                addslashes($key),
368
                addslashes($value)
369
            );
370
        }
371
        $databaseConfigContent = implode(",\n", $lines);
372
        $this->writeToFile($configPath, <<<PHP
373
<?php
374
375
use SilverStripe\\ORM\\DB;
376
377
DB::setConfig([
378
{$databaseConfigContent}
379
]);
380
381
PHP
382
        );
383
    }
384
385
    /**
386
     * Write yml files
387
     *
388
     * @param array $config
389
     */
390
    protected function writeConfigYaml($config)
391
    {
392
        // Escape user input for safe insertion into PHP file
393
        $locale = $this->ymlString($config['locale']);
394
        $projectDir = $this->getProjectDir();
395
396
        // Set either specified, or no theme
397
        if ($config['theme'] && $config['theme'] !== 'tutorial') {
398
            $theme = $this->ymlString($config['theme']);
399
            $themeYML = <<<YML
400
    - '\$public'
401
    - '$theme'
402
    - '\$default'
403
YML;
404
        } else {
405
            $themeYML = <<<YML
406
    - '\$public'
407
    - '\$default'
408
YML;
409
        }
410
411
        // Write theme.yml
412
        $this->writeToFile("{$projectDir}/_config/theme.yml", <<<YML
413
---
414
Name: mytheme
415
---
416
SilverStripe\\View\\SSViewer:
417
  themes:
418
$themeYML
419
SilverStripe\\i18n\\i18n:
420
  default_locale: '$locale'
421
YML
422
        );
423
    }
424
425
    /**
426
     * Escape yml string
427
     *
428
     * @param string $string
429
     * @return mixed
430
     */
431
    protected function ymlString($string)
432
    {
433
        // just escape single quotes using ''
434
        return str_replace("'", "''", $string);
435
    }
436
437
    /**
438
     * Write file to given location
439
     *
440
     * @param string $filename
441
     * @param string $content
442
     * @param bool $absolute If $filename is absolute path set to true
443
     * @return bool
444
     */
445
    public function writeToFile($filename, $content, $absolute = false)
446
    {
447
        // Get absolute / relative paths by either combining or removing base from path
448
        list($absolutePath, $relativePath) = $absolute
449
            ? [
450
                $filename,
451
                substr($filename, strlen($this->getBaseDir()))]
452
            : [
453
                $this->getBaseDir() . $filename,
454
                $filename
455
            ];
456
        $this->statusMessage("Setting up $relativePath");
457
458
        if ((@$fh = fopen($absolutePath, 'wb')) && fwrite($fh, $content) && fclose($fh)) {
459
            // Set permissions to writable
460
            @chmod($absolutePath, 0775);
461
            return true;
462
        }
463
        $this->error("Couldn't write to file $relativePath");
464
        return false;
465
    }
466
467
    /**
468
     * Ensure root .htaccess is setup
469
     */
470
    public function createHtaccess()
471
    {
472
        $start = "### SILVERSTRIPE START ###\n";
473
        $end = "\n### SILVERSTRIPE END ###";
474
475
        $base = dirname($_SERVER['SCRIPT_NAME']);
476
        $base = Convert::slashes($base, '/');
477
478
        if ($base != '.') {
479
            $baseClause = "RewriteBase '$base'\n";
480
        } else {
481
            $baseClause = "";
482
        }
483
        if (strpos(strtolower(php_sapi_name()), "cgi") !== false) {
484
            $cgiClause = "RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]\n";
485
        } else {
486
            $cgiClause = "";
487
        }
488
        $rewrite = <<<TEXT
489
# Deny access to templates (but allow from localhost)
490
<Files *.ss>
491
    Order deny,allow
492
    Deny from all
493
    Allow from 127.0.0.1
494
</Files>
495
496
# Deny access to IIS configuration
497
<Files web.config>
498
    Order deny,allow
499
    Deny from all
500
</Files>
501
502
# Deny access to YAML configuration files which might include sensitive information
503
<Files ~ "\.ya?ml$">
504
    Order allow,deny
505
    Deny from all
506
</Files>
507
508
# Route errors to static pages automatically generated by SilverStripe
509
ErrorDocument 404 /assets/error-404.html
510
ErrorDocument 500 /assets/error-500.html
511
512
<IfModule mod_rewrite.c>
513
514
    # Turn off index.php handling requests to the homepage fixes issue in apache >=2.4
515
    <IfModule mod_dir.c>
516
        DirectoryIndex disabled
517
        DirectorySlash On
518
    </IfModule>
519
520
    SetEnv HTTP_MOD_REWRITE On
521
    RewriteEngine On
522
    $baseClause
523
    $cgiClause
524
525
    # Deny access to potentially sensitive files and folders
526
    RewriteRule ^vendor(/|$) - [F,L,NC]
527
    RewriteRule ^\.env - [F,L,NC]
528
    RewriteRule silverstripe-cache(/|$) - [F,L,NC]
529
    RewriteRule composer\.(json|lock) - [F,L,NC]
530
    RewriteRule (error|silverstripe|debug)\.log - [F,L,NC]
531
532
    # Process through SilverStripe if no file with the requested name exists.
533
    # Pass through the original path as a query parameter, and retain the existing parameters.
534
    # Try finding framework in the vendor folder first
535
    RewriteCond %{REQUEST_URI} ^(.*)$
536
    RewriteCond %{REQUEST_FILENAME} !-f
537
    RewriteRule .* index.php
538
</IfModule>
539
TEXT;
540
541
        $htaccessPath = $this->getPublicDir() . '.htaccess';
542
        if (file_exists($htaccessPath)) {
543
            $htaccess = file_get_contents($htaccessPath);
544
545
            if (strpos($htaccess, '### SILVERSTRIPE START ###') === false
546
                && strpos($htaccess, '### SILVERSTRIPE END ###') === false
547
            ) {
548
                $htaccess .= "\n### SILVERSTRIPE START ###\n### SILVERSTRIPE END ###\n";
549
            }
550
551
            if (strpos($htaccess, '### SILVERSTRIPE START ###') !== false
552
                && strpos($htaccess, '### SILVERSTRIPE END ###') !== false
553
            ) {
554
                $start = substr($htaccess, 0, strpos($htaccess, '### SILVERSTRIPE START ###'))
555
                    . "### SILVERSTRIPE START ###\n";
556
                $end = "\n" . substr($htaccess, strpos($htaccess, '### SILVERSTRIPE END ###'));
557
            }
558
        }
559
560
        $this->writeToFile($htaccessPath, $start . $rewrite . $end, true);
561
    }
562
563
    /**
564
     * Writes basic configuration to the web.config for IIS
565
     * so that rewriting capability can be use.
566
     */
567
    public function createWebConfig()
568
    {
569
        $content = <<<TEXT
570
<?xml version="1.0" encoding="utf-8"?>
571
<configuration>
572
    <system.webServer>
573
        <security>
574
            <requestFiltering>
575
                <hiddenSegments applyToWebDAV="false">
576
                    <add segment="silverstripe-cache" />
577
                    <add segment="composer.json" />
578
                    <add segment="composer.lock" />
579
                </hiddenSegments>
580
                <fileExtensions allowUnlisted="true" >
581
                    <add fileExtension=".ss" allowed="false"/>
582
                    <add fileExtension=".yml" allowed="false"/>
583
                </fileExtensions>
584
            </requestFiltering>
585
        </security>
586
        <rewrite>
587
            <rules>
588
                <rule name="SilverStripe Clean URLs" stopProcessing="true">
589
                    <match url="^(.*)$" />
590
                    <conditions>
591
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
592
                    </conditions>
593
                    <action type="Rewrite" url="index.php" appendQueryString="true" />
594
                </rule>
595
            </rules>
596
        </rewrite>
597
    </system.webServer>
598
</configuration>
599
TEXT;
600
601
        $path = $this->getPublicDir() . 'web.config';
602
        $this->writeToFile($path, $content, true);
603
    }
604
605
    public function checkRewrite()
606
    {
607
        $params = http_build_query(['flush' => '']);
608
609
        $destinationURL = rtrim(BASE_URL, '/') . '/' . (
610
            $this->checkModuleExists('cms')
611
                ? "home/successfullyinstalled?$params"
612
                : "?$params"
613
        );
614
615
        // phpcs:disable
616
        echo <<<HTML
617
<li id="ModRewriteResult">Testing...</li>
618
<script>
619
    if (typeof $ == 'undefined') {
620
        document.getElementById('ModeRewriteResult').innerHTML = "I can't run jQuery ajax to set rewriting; I will redirect you to the homepage to see if everything is working.";
621
        setTimeout(function() {
622
            window.location = "$destinationURL";
623
        }, 10000);
624
    } else {
625
        $.ajax({
626
            method: 'get',
627
            url: 'InstallerTest/testrewrite',
628
            complete: function(response) {
629
                var r = response.responseText.replace(/[^A-Z]?/g,"");
630
                if (r === "OK") {
631
                    $('#ModRewriteResult').html("Friendly URLs set up successfully; I am now redirecting you to your SilverStripe site...")
632
                    setTimeout(function() {
633
                        window.location = "$destinationURL";
634
                    }, 2000);
635
                } else {
636
                    $('#ModRewriteResult').html("Friendly URLs are not working. This is most likely because a rewrite module isn't configured "
637
                        + "correctly on your site. You may need to get your web host or server administrator to do this for you: "
638
                        + "<ul>"
639
                        + "<li><strong>mod_rewrite</strong> or other rewrite module is enabled on your web server</li>"
640
                        + "<li><strong>AllowOverride All</strong> is set for the directory where SilverStripe is installed</li>"
641
                        + "</ul>");
642
                }
643
            }
644
        });
645
    }
646
</script>
647
<noscript>
648
    <li><a href="$destinationURL">Click here</a> to check friendly URLs are working. If you get a 404 then something is wrong.</li>
649
</noscript>
650
HTML;
651
        // phpcs:enable
652
    }
653
654
    /**
655
     * Show an installation status message.
656
     * The output differs depending on whether this is CLI or web based
657
     *
658
     * @param string $msg
659
     */
660
    public function statusMessage($msg)
661
    {
662
        echo "<li>$msg</li>\n";
663
        flush();
664
    }
665
666
    /**
667
     * @param $config
668
     */
669
    protected function sendInstallStats($config)
670
    {
671
        // Try to determine the database version from the helper
672
        $dbType = $config['db']['type'];
673
        $helper = $this->getDatabaseConfigurationHelper($dbType);
674
        if ($helper) {
675
            $databaseVersion = $dbType . ': ' . $helper->getDatabaseVersion($config['db']);
676
        } else {
677
            $databaseVersion = $dbType;
678
        }
679
680
        $args = http_build_query(array_filter([
681
            'SilverStripe' => $config['version'],
682
            'PHP' => phpversion(),
683
            'Database' => $databaseVersion,
684
            'WebServer' => $this->findWebserver(),
685
            'ID' => empty($_SESSION['StatsID']) ? null : $_SESSION['StatsID']
686
        ]));
687
        $url = "http://ss2stat.silverstripe.com/Installation/add?{$args}";
688
        @$_SESSION['StatsID'] = file_get_contents($url);
689
    }
690
}
691