AppInstance   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 317
Duplicated Lines 7.89 %

Coupling/Cohesion

Components 4
Dependencies 6

Importance

Changes 0
Metric Value
dl 25
loc 317
rs 9.2
c 0
b 0
f 0
wmc 40
lcom 4
cbo 6

18 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 48 12
A isEnabled() 0 4 2
A getConfigDefaults() 0 4 1
A init() 0 3 1
A onReady() 0 3 1
A getInstance() 0 4 1
A RPCall() 0 8 3
A getConfig() 0 4 1
A getName() 0 4 1
A broadcastCall() 8 9 2
A singleCall() 8 9 2
A directCall() 9 10 2
A log() 0 4 1
A handleRequest() 0 5 2
A beginRequest() 0 8 2
A handleStatus() 0 12 4
A onConfigUpdated() 0 3 1
A onShutdown() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AppInstance 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 AppInstance, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace PHPDaemon\Core;
3
4
use PHPDaemon\Config;
5
use PHPDaemon\Thread;
6
7
/**
8
 * Application instance
9
 * @package PHPDaemon\Core
10
 * @author  Vasily Zorin <[email protected]>
11
 */
12
class AppInstance
13
{
14
    use \PHPDaemon\Traits\ClassWatchdog;
15
    use \PHPDaemon\Traits\StaticObjectWatchdog;
16
17
    /**
18
     * Event: config updated
19
     */
20
    const EVENT_CONFIG_UPDATED = 1;
21
22
    /**
23
     * Event: graceful shutdown
24
     */
25
    const EVENT_GRACEFUL_SHUTDOWN = 2;
26
27
    /**
28
     * Event: shutdown
29
     */
30
    const EVENT_SHUTDOWN = 3;
31
32
    /**
33
     * @var boolean If true, it's allowed to be run without defined config section'
34
     */
35
    public static $runOnDemand = true;
36
37
    /**
38
     * @var string Optional passphrase
39
     */
40
    public $passphrase;
41
42
    /**
43
     * @var boolean Ready to run?
44
     */
45
    public $ready = false;
46
47
    /**
48
     * @var object Related config section
49
     */
50
    public $config;
51
52
    /**
53
     * @var boolean Is RPC enabled?
54
     */
55
    public $enableRPC = false;
56
57
    /**
58
     * @var null|string Default class of incoming requests
59
     */
60
    public $requestClass;
61
62
    /**
63
     * @var string Instance name
64
     */
65
    protected $name;
66
67
    /**
68
     * Application constructor
69
     * @param  string $name Instance name
70
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
71
     */
72
    public function __construct($name = '')
73
    {
74
        $this->name = $name;
75
        $appName = '\\' . get_class($this);
76
        $fullname = Daemon::$appResolver->getAppFullName($appName, $this->name);
77
        //Daemon::$process->log($fullname . ' instantiated.');
78
79
        if ($this->requestClass === null) {
80
            $this->requestClass = get_class($this) . 'Request';
81
            if (!class_exists($this->requestClass)) {
82
                $this->requestClass = null;
83
            }
84
        }
85
86
        if (!isset(Daemon::$appInstances[$appName])) {
87
            Daemon::$appInstances[$appName] = [];
88
        }
89
        Daemon::$appInstances[$appName][$this->name] = $this;
90
91
        if (!isset(Daemon::$config->{$fullname})) {
92
            Daemon::$config->{$fullname} = new Config\Section;
93
        } else {
94
            if (!isset(Daemon::$config->{$fullname}->enable) && !isset(Daemon::$config->{$fullname}->disable)) {
95
                Daemon::$config->{$fullname}->enable = new Config\Entry\Generic;
96
                Daemon::$config->{$fullname}->enable->setValue(true);
97
            }
98
        }
99
100
        $this->config = Daemon::$config->{$fullname};
101
        if ($this->isEnabled()) {
102
            Daemon::$process->log($appName . ($name ? ":{$name}" : '') . ' up.');
103
        }
104
105
        $defaults = $this->getConfigDefaults();
106
        if ($defaults) {
107
            /** @noinspection PhpUndefinedMethodInspection */
108
            $this->config->imposeDefault($defaults);
109
        }
110
111
        $this->init();
112
113
        if (Daemon::$process instanceof Thread\Worker) {
114
            if (!$this->ready) {
115
                $this->ready = true;
116
                $this->onReady();
117
            }
118
        }
119
    }
120
121
    /**
122
     * Returns whether if this application is enabled
123
     * @return boolean
124
     */
125
    public function isEnabled()
126
    {
127
        return isset($this->config->enable->value) && $this->config->enable->value;
128
    }
129
130
    /**
131
     * Function to get default config options from application
132
     * Override to set your own
133
     * @return boolean
134
     */
135
    protected function getConfigDefaults()
136
    {
137
        return false;
138
    }
139
140
    /**
141
     * Called when creates instance of the application
142
     * @return void
143
     */
144
    protected function init()
145
    {
146
    }
147
148
    /**
149
     * Called when the worker is ready to go
150
     * @return void
151
     */
152
    public function onReady()
153
    {
154
    }
155
156
    /**
157
     * @param  string $name Instance name
158
     * @param  boolean $spawn If true, we spawn an instance if absent
159
     * @return AppInstance
160
     */
161
    public static function getInstance($name, $spawn = true)
162
    {
163
        return Daemon::$appResolver->getInstanceByAppName('\\' . static::class, $name, $spawn);
164
    }
165
166
    /**
167
     * Function handles incoming Remote Procedure Calls
168
     * You can override it
169
     * @param  string $method Method name
170
     * @param  array $args Arguments
171
     * @return mixed          Result
172
     */
173
    public function RPCall($method, $args)
174
    {
175
        if (!$this->enableRPC || !is_callable([$this, $method])) {
176
            return false;
177
        }
178
        $func = [$this, $method];
179
        return $func(...$args);
180
    }
181
182
    /**
183
     * Returns a config section
184
     * @return Config\Section
185
     */
186
    public function getConfig()
187
    {
188
        return $this->config;
189
    }
190
191
    /**
192
     * Returns the instance name
193
     * @return string
194
     */
195
    public function getName()
196
    {
197
        return $this->name;
198
    }
199
200
    /**
201
     * Send broadcast RPC
202
     * You can override it
203
     * @param  string $method Method name
204
     * @param  array $args Arguments
205
     * @param  callable $cb Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
206
     * @return boolean Success
207
     */
208 View Code Duplication
    public function broadcastCall($method, $args = [], $cb = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
209
    {
210
        return Daemon::$process->IPCManager->sendBroadcastCall(
211
            '\\' . get_class($this) . ($this->name !== '' ? ':' . $this->name : ''),
212
            $method,
213
            $args,
214
            $cb
215
        );
216
    }
217
218
    /**
219
     * Send RPC, executed once in any worker
220
     * You can override it
221
     * @param  string $method Method name
222
     * @param  array $args Arguments
223
     * @param  mixed $cb Callback
224
     * @return boolean Success
225
     */
226 View Code Duplication
    public function singleCall($method, $args = [], $cb = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
227
    {
228
        return Daemon::$process->IPCManager->sendSingleCall(
229
            '\\' . get_class($this) . ($this->name !== '' ? ':' . $this->name : ''),
230
            $method,
231
            $args,
232
            $cb
233
        );
234
    }
235
236
    /**
237
     * Send RPC, executed once in certain worker
238
     * You can override it
239
     * @param  integer $workerId Worker Id
240
     * @param  string $method Method name
241
     * @param  array $args Arguments
242
     * @param  mixed $cb Callback
243
     * @return boolean Success
244
     */
245 View Code Duplication
    public function directCall($workerId, $method, $args = [], $cb = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
246
    {
247
        return Daemon::$process->IPCManager->sendDirectCall(
248
            $workerId,
249
            '\\' . get_class($this) . ($this->name !== '' ? ':' . $this->name : ''),
250
            $method,
251
            $args,
252
            $cb
253
        );
254
    }
255
256
    /**
257
     * Log something
258
     * @param  string $message Message
259
     * @return void
260
     */
261
    public function log($message)
262
    {
263
        Daemon::log(get_class($this) . ': ' . $message);
264
    }
265
266
    /**
267
     * Handle the request
268
     * @param  object $parent Parent request
269
     * @param  object $upstream Upstream application
270
     * @return object Request
271
     */
272
    public function handleRequest($parent, $upstream)
273
    {
274
        $req = $this->beginRequest($parent, $upstream);
275
        return $req ?: $parent;
276
    }
277
278
    /**
279
     * Create Request instance
280
     * @param  object $req Generic
281
     * @param  object $upstream Upstream application instance
282
     * @return object Request
0 ignored issues
show
Documentation introduced by
Should the return type not be false|object?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
283
     */
284
    public function beginRequest($req, $upstream)
285
    {
286
        if (!$this->requestClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->requestClass of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
287
            return false;
288
        }
289
        $className = $this->requestClass;
290
        return new $className($this, $upstream, $req);
291
    }
292
293
    /**
294
     * Handle the worker status
295
     * @param  integer $ret Status code
296
     * @return boolean Result
297
     */
298
    public function handleStatus($ret)
299
    {
300
        if ($ret === self::EVENT_CONFIG_UPDATED) {
301
            $this->onConfigUpdated();
302
            return true;
303
        } elseif ($ret === self::EVENT_GRACEFUL_SHUTDOWN) {
304
            return $this->onShutdown(true);
305
        } elseif ($ret === self::EVENT_SHUTDOWN) {
306
            return $this->onShutdown();
307
        }
308
        return false;
309
    }
310
311
    /**
312
     * Called when worker is going to update configuration
313
     * @todo call it only when application section config is changed
314
     * @return void
315
     */
316
    public function onConfigUpdated()
317
    {
318
    }
319
320
    /**
321
     * Called when application instance is going to shutdown
322
     * @return boolean Ready to shutdown?
323
     */
324
    protected function onShutdown($graceful = false)
325
    {
326
        return true;
327
    }
328
}
329