Completed
Pull Request — master (#393)
by De Cramer
02:39
created

AbstractApplication::run()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 6.3949

Importance

Changes 0
Metric Value
dl 0
loc 57
ccs 21
cts 27
cp 0.7778
rs 8.3158
c 0
b 0
f 0
cc 6
nc 6
nop 0
crap 6.3949

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
4
namespace eXpansion\Framework\Core\Services\Application;
5
6
use eXpansion\Framework\Core\Services\Console;
7
use eXpansion\Framework\Core\Services\DedicatedConnection\Factory;
8
use Maniaplanet\DedicatedServer\Connection;
9
use Propel\Runtime\Connection\Exception\ConnectionException;
10
use Propel\Runtime\Propel;
11
use Psr\Log\LoggerInterface;
12
use Symfony\Component\Console\Output\OutputInterface;
13
14
abstract class AbstractApplication implements RunInterface
15
{
16
    /** Base eXpansion callbacks. */
17
    const EVENT_BEFORE_INIT = "expansion.before_init";
18
    const EVENT_SWITCH_TO_SCRIPT = "expansion.switched_to_script";
19
    const EVENT_AFTER_INIT = "expansion.after_init";
20
    const EVENT_READY = "expansion.ready";
21
    const EVENT_STOP = "expansion.stop";
22
23
    const EXPANSION_VERSION = "dev";
24
25
    /** @var Factory */
26
    protected $factory;
27
28
    /** @var DispatcherInterface */
29
    protected $dispatcher;
30
31
    /** @var Console */
32
    protected $console;
33
34
    /** @var bool */
35
    protected $isRunning = true;
36
    /**
37
     * @var LoggerInterface
38
     */
39
    private $logger;
40
41
    /**
42
     * AbstractApplication constructor.
43
     *
44
     * @param DispatcherInterface $dispatcher
45
     * @param Factory $factory
46
     * @param Console $output
47
     * @param LoggerInterface $logger
48
     */
49 10
    public function __construct(
50
        DispatcherInterface $dispatcher,
51
        Factory $factory,
52
        Console $output,
53
        LoggerInterface $logger
54
    ) {
55 10
        $this->factory = $factory;
56 10
        $this->dispatcher = $dispatcher;
57 10
        $this->console = $output;
58 10
        $this->logger = $logger;
59 10
    }
60
61
    /**
62
     * Initialize eXpansion.
63
     *
64
     * @param OutputInterface $console
65
     *
66
     * @return $this|mixed
67
     * @throws \Maniaplanet\DedicatedServer\Xmlrpc\TransportException
68
     */
69 1
    public function init(OutputInterface $console)
70
    {
71 1
        $this->checkPhpExtensions($console);
72
73 1
        $this->console->init($console, $this->dispatcher);
74 1
        $this->dispatcher->dispatch(self::EVENT_BEFORE_INIT, []);
75
76 1
        $this->factory->createConnection();
77
78 1
        $this->console->writeln("Running preflight checks...");
79 1
        $this->factory->getConnection()->enableCallbacks(true);
80
81
        // need to send this for scripts to start callback handling
82
        try {
83 1
            $this->factory->getConnection()->triggerModeScriptEvent("XmlRpc.EnableCallbacks", ["True"]);
84 1
            $this->dispatcher->dispatch(self::EVENT_SWITCH_TO_SCRIPT, []);
85
        } catch (\Exception $exception) {
86
            $this->factory->getConnection()->saveMatchSettings('MatchSettings/eXpansion-mode-fail-'.date(DATE_ISO8601).'.txt');
87
88
            $console->writeln("");
89
            $msg = "<error>Failed to start eXpansion. This is probably due to the server not being in script mode.";
90
            $msg .= " Please switch <info>game_mode</info> to <info>0</info> and configure the proper <info>script_name</info> in your match setting file";
91
            $msg .= "</error>";
92
            $console->writeln("$msg");
93
            $console->writeln("");
94
95
            throw $exception;
96
        }
97
98 1
        $this->dispatcher->init($this->factory->getConnection());
99
100 1
        $this->dispatcher->dispatch(self::EVENT_AFTER_INIT, []);
101 1
        return $this;
102
    }
103
104 1
    protected function checkPhpExtensions(OutputInterface $console)
105
    {
106
        $extensions = array(
107 1
            'openssl' => 'extension=php_openssl.dll',
108
            'curl' => 'extension=curl.dll',
109
        );
110 1
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
111
            $extensions['com_dotnet'] = 'extension=php_com_dotnet.dll';
112
        }
113
114 1
        $status = true;
115 1
        $showIni = false;
116 1 View Code Duplication
        foreach ($extensions as $extension => $description) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
117 1
            if (!extension_loaded($extension)) {
118
                $console->writeln(
119
                    "<error>eXpansion needs PHP extension $extension to run. Enable it to run eXpansion => " . $description . "</error>"
120
                );
121
                $status = false;
122
                $showIni = true;
123
            }
124
        }
125
126
        $recommend = array(
127 1
            'xmlrpc' => "It will have better performances !",
128
        );
129 1 View Code Duplication
        foreach ($recommend as $extension => $reason) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130 1
            if (!extension_loaded($extension)) {
131
                $console->writeln(
132
                    "<error>eXpansion works better with PHP extension</error> <info>$extension</info>: " . $reason . ""
133
                );
134
                $showIni = true;
135
            }
136
        }
137
138 1
        if ($showIni) {
139
            $console->writeln('<info>[PHP] PHP is using fallowing ini file :</info> "'. php_ini_loaded_file() .'"');
140
            sleep(5);
141
        }
142 1
        return $status;
143
    }
144
145
    /**
146
     * Run eXpansion
147
     *
148
     * @inheritdoc
149
     */
150 2
    public function run()
151
    {
152
        // Time each cycle needs to take in microseconds. Wrunning 60 cycles per seconds to have optimal response time.
153 2
        $cycleTime = (1 / 60) * 1000000;
154
155
        // Running GC collect every 5 minutes should be sufficient.;
156 2
        $gcCycleTime = 60 * 5;
157
158
        // Time when we will force gc cycles.
159 2
        $maxGcCycleTime = 60 * 20;
160
161
        // Last time garbage collector ran. Assume that at start it ran.
162 2
        $lastGcTime = time();
163
164 2
        $this->console->writeln("preflight checks OK.");
165
166 2
        $this->dispatcher->dispatch(self::EVENT_READY, []);
167
168 2
        $this->console->writeln("And takeoff");
169
170
        do {
171 2
            $startTime = microtime(true);
172
173
            // Run the actuall application
174 2
            $this->executeRun();
175
176 2
            $endTime = microtime(true);
177 2
            $delay = $cycleTime - (($endTime - $startTime) * 1000000);
178
179
            // If we got lot's of time and it's been a while since last GC collect let's run a garbage collector
180
            // cycle this iteration.
181 2
            if ($delay > $cycleTime / 2 && $lastGcTime < (time() - $gcCycleTime)) {
182
                // PHP does this automatically as well but in some mysterious ways it can sometimes keep in memory
183
                // hundred of mb's before running it.
184
                gc_collect_cycles();
185
                $lastGcTime = time();
186
187
                // Renew delay so that this iteration isn't much slower then the others
188
                $endTime = microtime(true);
189
                $delay = $cycleTime - (($endTime - $startTime) * 1000000);
190
            }
191
192 2
            if ($lastGcTime < (time() - $maxGcCycleTime)) {
193
                //It's been a while since last Garbage collection forcing it to go even through the application is
194
                // running slow.
195
                gc_collect_cycles();
196
                $lastGcTime = time();
197
198 2
            } elseif ($delay > 0) {
199 2
                usleep($delay);
200
            }
201 2
        } while ($this->isRunning);
202
203 2
        $this->factory->getConnection()->sendHideManialinkPage(null);
204 2
        $this->factory->getConnection()->triggerModeScriptEvent("Shootmania.UI.ResetProperties", []);
205 2
        $this->factory->getConnection()->triggerModeScriptEvent("Trackmania.UI.ResetProperties", []);
206 2
    }
207
208
    /**
209
     * Stop eXpansion.
210
     */
211 2
    public function stopApplication()
212
    {
213 2
        $this->dispatcher->dispatch(self::EVENT_STOP, []);
214 2
        $this->isRunning = false;
215 2
    }
216
217
    abstract protected function executeRun();
218
}
219