Passed
Pull Request — master (#20021)
by Wilmer
13:16 queued 04:06
created

ServeController   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 111
Duplicated Lines 0 %

Test Coverage

Coverage 89.74%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 46
dl 0
loc 111
ccs 35
cts 39
cp 0.8974
rs 10
c 1
b 0
f 0
wmc 15

4 Methods

Rating   Name   Duplication   Size   Complexity  
A isAddressTaken() 0 9 2
B actionIndex() 0 42 11
A options() 0 6 1
A optionAliases() 0 6 1
1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\console\controllers;
9
10
use Yii;
11
use yii\console\Controller;
12
use yii\helpers\Console;
13
14
/**
15
 * Runs PHP built-in web server.
16
 *
17
 * In order to access server from remote machines use 0.0.0.0:8000. That is especially useful when running server in
18
 * a virtual machine.
19
 *
20
 * @author Alexander Makarov <[email protected]>
21
 * @since 2.0.7
22
 */
23
class ServeController extends Controller
24
{
25
    const EXIT_CODE_NO_DOCUMENT_ROOT = 2;
26
    const EXIT_CODE_NO_ROUTING_FILE = 3;
27
    const EXIT_CODE_ADDRESS_TAKEN_BY_ANOTHER_SERVER = 4;
28
    const EXIT_CODE_ADDRESS_TAKEN_BY_ANOTHER_PROCESS = 5;
29
30
    /**
31
     * @var int port to serve on.
32
     */
33
    public $port = 8080;
34
    /**
35
     * @var string path or [path alias](guide:concept-aliases) to directory to serve
36
     */
37
    public $docroot = '@app/web';
38
    /**
39
     * @var string path or [path alias](guide:concept-aliases) to router script.
40
     * See https://www.php.net/manual/en/features.commandline.webserver.php
41
     */
42
    public $router;
43
44
45
    /**
46
     * Runs PHP built-in web server.
47
     *
48
     * @param string $address address to serve on. Either "host" or "host:port".
49
     *
50
     * @return int
51
     */
52 5
    public function actionIndex($address = 'localhost')
53
    {
54 5
        $documentRoot = Yii::getAlias($this->docroot);
55 5
        $router = $this->router !== null ? Yii::getAlias($this->router) : null;
56
57 5
        if (strpos($address, ':') === false) {
58 4
            $address = $address . ':' . $this->port;
59
        }
60
61 5
        if (!is_dir($documentRoot)) {
0 ignored issues
show
Bug introduced by
It seems like $documentRoot can also be of type false; however, parameter $filename of is_dir() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

61
        if (!is_dir(/** @scrutinizer ignore-type */ $documentRoot)) {
Loading history...
62 1
            $this->stdout("Document root \"$documentRoot\" does not exist.\n", Console::FG_RED);
63 1
            return self::EXIT_CODE_NO_DOCUMENT_ROOT;
64
        }
65
66 4
        if ($this->isAddressTaken($address)) {
67 1
            $this->stdout("http://$address is taken by another process.\n", Console::FG_RED);
68 1
            return self::EXIT_CODE_ADDRESS_TAKEN_BY_ANOTHER_PROCESS;
69
        }
70
71 3
        if ($this->router !== null && !file_exists($router)) {
0 ignored issues
show
Bug introduced by
It seems like $router can also be of type false and null; however, parameter $filename of file_exists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

71
        if ($this->router !== null && !file_exists(/** @scrutinizer ignore-type */ $router)) {
Loading history...
72 1
            $this->stdout("Routing file \"$router\" does not exist.\n", Console::FG_RED);
73 1
            return self::EXIT_CODE_NO_ROUTING_FILE;
74
        }
75
76 2
        $this->stdout("Server started on http://{$address}/\n");
77 2
        $this->stdout("Document root is \"{$documentRoot}\"\n");
78 2
        if ($this->router) {
79 1
            $this->stdout("Routing file is \"$router\"\n");
80
        }
81 2
        $this->stdout("Quit the server with CTRL-C or COMMAND-C.\n");
82
83 2
        $command = '"' . PHP_BINARY . '"' . " -S {$address} -t \"{$documentRoot}\"";
84
85 2
        if ($this->router !== null && $router !== '') {
86 1
            $command .= " -r \"{$router}\"";
87
        }
88
89 2
        if (YII_ENV === 'test') {
0 ignored issues
show
introduced by
The condition yii\console\controllers\YII_ENV === 'test' is always true.
Loading history...
90 2
            return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type integer.
Loading history...
91
        }
92
93
        passthru($command);
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 1
    public function options($actionID)
100
    {
101 1
        return array_merge(parent::options($actionID), [
102 1
            'docroot',
103
            'router',
104
            'port',
105
        ]);
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     * @since 2.0.8
111
     */
112 1
    public function optionAliases()
113
    {
114 1
        return array_merge(parent::optionAliases(), [
115 1
            't' => 'docroot',
116
            'p' => 'port',
117
            'r' => 'router',
118
        ]);
119
    }
120
121
    /**
122
     * @param string $address server address
123
     * @return bool if address is already in use
124
     */
125 3
    protected function isAddressTaken($address)
126
    {
127 3
        list($hostname, $port) = explode(':', $address);
128 3
        $fp = @fsockopen($hostname, $port, $errno, $errstr, 3);
0 ignored issues
show
Bug introduced by
$port of type string is incompatible with the type integer expected by parameter $port of fsockopen(). ( Ignorable by Annotation )

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

128
        $fp = @fsockopen($hostname, /** @scrutinizer ignore-type */ $port, $errno, $errstr, 3);
Loading history...
129 3
        if ($fp === false) {
130 3
            return false;
131
        }
132
        fclose($fp);
133
        return true;
134
    }
135
}
136