Issues (910)

framework/console/controllers/ServeController.php (2 issues)

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
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)) {
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
        $this->runCommand($command);
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 1
    public function options($actionID)
96
    {
97 1
        return array_merge(parent::options($actionID), [
98 1
            'docroot',
99 1
            'router',
100 1
            'port',
101 1
        ]);
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     * @since 2.0.8
107
     */
108 1
    public function optionAliases()
109
    {
110 1
        return array_merge(parent::optionAliases(), [
111 1
            't' => 'docroot',
112 1
            'p' => 'port',
113 1
            'r' => 'router',
114 1
        ]);
115
    }
116
117
    /**
118
     * @param string $address server address
119
     * @return bool if address is already in use
120
     */
121 3
    protected function isAddressTaken($address)
122
    {
123 3
        list($hostname, $port) = explode(':', $address);
124 3
        $fp = @fsockopen($hostname, $port, $errno, $errstr, 3);
0 ignored issues
show
$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

124
        $fp = @fsockopen($hostname, /** @scrutinizer ignore-type */ $port, $errno, $errstr, 3);
Loading history...
125 3
        if ($fp === false) {
126 3
            return false;
127
        }
128
        fclose($fp);
129
        return true;
130
    }
131
132
    protected function runCommand($command)
133
    {
134
        passthru($command);
135
    }
136
}
137