成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

"php artisan serve"到底干了什么

TANKING / 2127人閱讀

摘要:最近看了一下這個(gè)框架,寫(xiě)點(diǎn)東西當(dāng)個(gè)筆記。函數(shù)會(huì)迭代屬性為的,逐一將其注冊(cè),的方法繼承自父類,關(guān)鍵的就是在這個(gè)里注冊(cè)的。

最近看了一下 laravel 這個(gè)框架,寫(xiě)點(diǎn)東西當(dāng)個(gè)筆記。跟著官網(wǎng)上的說(shuō)明 install 好一個(gè)項(xiàng)目后,在項(xiàng)目根目錄執(zhí)行命令php artisan serve就可以開(kāi)啟一個(gè)簡(jiǎn)易的服務(wù)器進(jìn)行開(kāi)發(fā),這個(gè)命令到底做了什么,看了一下代碼,在這里簡(jiǎn)要描述一下自己的看法。

先說(shuō)明一下,這里項(xiàng)目 install 的方法不是安裝 laravel/installer,而是composer create-project --prefer-dist laravel/laravel blog,寫(xiě)筆記的時(shí)候 laravel 的版本還是 5.5,以后版本更新后可能就不一樣了。

artisan 實(shí)際上是項(xiàng)目根目錄下的一個(gè) php 腳本,而且默認(rèn)是有執(zhí)行權(quán)限的,所以命令其實(shí)可以簡(jiǎn)寫(xiě)成artisan serve,腳本的代碼行數(shù)很少,實(shí)際上就十幾行:

#!/usr/bin/env php
make(IlluminateContractsConsoleKernel::class);

$status = $kernel->handle(
    $input = new SymfonyComponentConsoleInputArgvInput,
    new SymfonyComponentConsoleOutputConsoleOutput
);

$kernel->terminate($input, $status);

exit($status);

代碼里,require __DIR__."/vendor/autoload.php";的 autoload.php 文件是 composer 生成的文件,實(shí)際用處就是利用 php 提供 spl_autoload_register 函數(shù)注冊(cè)一個(gè)方法,讓執(zhí)行時(shí)遇到一個(gè)未聲明的類時(shí)會(huì)自動(dòng)將包含類定義的文件包含進(jìn)來(lái),舉個(gè)例子就是腳本當(dāng)中并沒(méi)有包含任何文件,但卻可以直接 new 一個(gè) SymfonyComponentConsoleInputArgvInput 對(duì)象,就是這個(gè) autoload.php 的功勞了。

接下來(lái)的這一行,$app = require_once __DIR__."/bootstrap/app.php";,在腳本里實(shí)例化一個(gè) IlluminateFoundationApplication 對(duì)象,將幾個(gè)重要的接口和類綁定在一起,然后將 Application 對(duì)象返回,其中接下來(lái)用到的 IlluminateContractsConsoleKernel::class 就是在這里和 AppConsoleKernel::class 綁定在一起的。

$kernel = $app->make(IlluminateContractsConsoleKernel::class);,直觀的解釋就是讓 $app 制造出一個(gè) AppConsoleKernel::class 實(shí)例(雖然括號(hào)里是 IlluminateContractsConsoleKernel::class,但由于跟這個(gè)接口綁定在一起的是 AppConsoleKernel::class 所以實(shí)際上 $kernel 實(shí)際上是 AppConsoleKernel::class)。

之后的就是整個(gè)腳本中最重要的一行了,調(diào)用 $kernelhandle 方法,AppConsoleKernel::class這個(gè)類在項(xiàng)目根目錄下的 app/Console 文件夾里,這個(gè)類并沒(méi)有實(shí)現(xiàn) handle 方法,實(shí)際上調(diào)用的是它的父類的 handle方法:


IlluminateFoundationConsoleKernelhandler 方法如下:

public function handle($input, $output = null)
{
    try {
        $this->bootstrap();

        return $this->getArtisan()->run($input, $output);
    } catch (Exception $e) {
        $this->reportException($e);

        $this->renderException($output, $e);

        return 1;
    } catch (Throwable $e) {
        $e = new FatalThrowableError($e);

        $this->reportException($e);

        $this->renderException($output, $e);

        return 1;
    }
}

bootstrap 方法如下:

public function bootstrap()
{
    if (! $this->app->hasBeenBootstrapped()) {
        $this->app->bootstrapWith($this->bootstrappers());
    }

    $this->app->loadDeferredProviders();

    if (! $this->commandsLoaded) {
        $this->commands();

        $this->commandsLoaded = true;
    }
}

先從 bootstrap 方法說(shuō)起, $kernel 對(duì)象里的成員 $app 實(shí)際上就是之前實(shí)例化的 IlluminateFoundationApplication ,所以調(diào)用的 bootstrapWith 方法是這樣的:

public function bootstrapWith(array $bootstrappers)
{
    $this->hasBeenBootstrapped = true;

    foreach ($bootstrappers as $bootstrapper) {
        $this["events"]->fire("bootstrapping: ".$bootstrapper, [$this]);

        $this->make($bootstrapper)->bootstrap($this);

        $this["events"]->fire("bootstrapped: ".$bootstrapper, [$this]);
    }
}

那么串聯(lián)起來(lái)實(shí)際上 bootstrap 方法里的這一句 $this->app->bootstrapWith($this->bootstrappers()); 就是實(shí)例化了 $kernel$bootstrappers 包含的所有類并且調(diào)用了這些對(duì)象里的 bootstrap 方法:

protected $bootstrappers = [
    IlluminateFoundationBootstrapLoadEnvironmentVariables::class,
    IlluminateFoundationBootstrapLoadConfiguration::class,
    IlluminateFoundationBootstrapHandleExceptions::class,
    IlluminateFoundationBootstrapRegisterFacades::class,
    IlluminateFoundationBootstrapSetRequestForConsole::class,
    IlluminateFoundationBootstrapRegisterProviders::class,
    IlluminateFoundationBootstrapBootProviders::class,
];

其中 IlluminateFoundationBootstrapRegisterProviders::classbootstrap 會(huì)調(diào)用 IlluminateFoundationApplication 實(shí)例的 registerConfiguredProviders 方法,這個(gè)方法會(huì)將讀取到的項(xiàng)目配置里的配置項(xiàng)(項(xiàng)目根目錄下的 config/app.php 文件里的 providers)放入一個(gè) IlluminateSupportCollection 對(duì)象中,然后和緩存合并并且排除掉其中的重復(fù)項(xiàng)作為一個(gè) ProviderRepository 實(shí)例的 load 方法的參數(shù),這個(gè) load 方法里會(huì)將 $defer 屬性不為 true 的 Provider 類使用 IlluminateFoundationApplicationregister 方法注冊(cè)(最簡(jiǎn)單理解就是 new 一個(gè)該 Provider 對(duì)象然后調(diào)用該對(duì)象的 register 方法)。

對(duì) artisan 十分重要的一個(gè) ProviderArtisanServiceProvider)的注冊(cè)過(guò)程非常繞。

項(xiàng)目根目錄下的 config/app.php 里有個(gè) ConsoleSupportServiceProvider$defer 屬性為 true ,所以不會(huì)在上面提到的過(guò)程中馬上注冊(cè),而會(huì)在 bootstrap 中的這句 $this->app->loadDeferredProviders(); 里注冊(cè)。

loadDeferredProviders 函數(shù)會(huì)迭代 $defer 屬性為 true 的 Provider,逐一將其注冊(cè),ConsoleSupportServiceProviderregister 方法繼承自父類 AggregateServiceProvider ,關(guān)鍵的 ArtisanServiceProvider 就是在這個(gè) register 里注冊(cè)的。

ArtisanServiceProviderregister 方法如下:

public function register()
{
    $this->registerCommands(array_merge(
        $this->commands, $this->devCommands
    ));
}

protected function registerCommands(array $commands)
{
    foreach (array_keys($commands) as $command) {
        call_user_func_array([$this, "register{$command}Command"], []);
    }

    $this->commands(array_values($commands));
}

這個(gè)方法會(huì)調(diào)用自身的方法 registerCommands , registerCommands 會(huì)調(diào)用 ArtisanServiceProvider 里所有名字類似 "register{$command}Command" 的方法,這些方法會(huì)在 IlluminateFoundationApplication 這個(gè)容器(即 IlluminateFoundationApplication 實(shí)例,這個(gè)類繼承了 IlluminateContainerContainer)中注冊(cè)命令,當(dāng)需要使用這些命令時(shí)就會(huì)返回一個(gè)這些命令的實(shí)例:

protected function registerServeCommand()
{
    $this->app->singleton("command.serve", function () {
        return new ServeCommand;
    });
}

以 serve 這個(gè)命令為例,這個(gè)方法的用處就是當(dāng)需要從容器里取出 command.serve 時(shí)就會(huì)得到一個(gè) ServeCommand 實(shí)例。

registerCommands 方法里還有一個(gè)重要的方法調(diào)用, $this->commands(array_values($commands)); , ArtisanServiceProvider 里并沒(méi)有這個(gè)方法的聲明,所以這個(gè)方法其實(shí)是在其父類 ServiceProvider 實(shí)現(xiàn)的:

use IlluminateConsoleApplication as Artisan;

......

public function commands($commands)
{
    $commands = is_array($commands) ? $commands : func_get_args();

    Artisan::starting(function ($artisan) use ($commands) {
        $artisan->resolveCommands($commands);
    });
}

Artisan::starting 這個(gè)靜態(tài)方法的調(diào)用會(huì)將括號(hào)里的匿名函數(shù)添加到 Artisan 類(實(shí)際上是 IlluminateConsoleApplication 類,不過(guò)引入時(shí)起了個(gè)別名)的靜態(tài)成員 $bootstrappers 里,這個(gè)會(huì)在接下來(lái)再提及到。

接下來(lái)回到 IlluminateFoundationConsoleKernelhandler 方法,return $this->getArtisan()->run($input, $output); , getArtisan 方法如下:

protected function getArtisan()
{
    if (is_null($this->artisan)) {
        return $this->artisan = (new Artisan($this->app, $this->events, $this->app->version()))
                            ->resolveCommands($this->commands);
    }

    return $this->artisan;
}

該方法會(huì) new 出一個(gè) Artisan 對(duì)象, 而這個(gè)類會(huì)在自己的構(gòu)造函數(shù)調(diào)用 bootstrap 方法:

protected function bootstrap()
{
    foreach (static::$bootstrappers as $bootstrapper) {
        $bootstrapper($this);
    }
}

這時(shí)候剛才被提及到的匿名函數(shù)就是在這里發(fā)揮作用,該匿名函數(shù)的作用就是調(diào)用 Artisan 對(duì)象的 resolveCommands 方法:

public function resolve($command)
{
    return $this->add($this->laravel->make($command));
}

public function resolveCommands($commands)
{
    $commands = is_array($commands) ? $commands : func_get_args();

    foreach ($commands as $command) {
        $this->resolve($command);
    }

    return $this;
}

resolveCommands 方法中迭代的 $commands 參數(shù)實(shí)際上是 ArtisanServiceProvider 里的兩個(gè)屬性 $commands$devCommands merge 在一起后取出值的數(shù)組(merge 發(fā)生在 ArtisanServiceProviderregister 方法, registerCommands 中使用 array_values 取出其中的值),所以對(duì)于 serve 這個(gè)命令,實(shí)際上發(fā)生的是 $this->resolve("command.serve");,而在之前已經(jīng)提到過(guò),ArtisanServiceProvider"register{$command}Command" 的方法會(huì)在容器里注冊(cè)命令,那么 resolve 方法的結(jié)果將會(huì)是將一個(gè) new 出來(lái) ServeCommand 對(duì)象作為參數(shù)被傳遞到 add 方法:

public function add(SymfonyCommand $command)
{
    if ($command instanceof Command) {
        $command->setLaravel($this->laravel);
    }

    return $this->addToParent($command);
}

protected function addToParent(SymfonyCommand $command)
{
    return parent::add($command);
}

add 方法實(shí)際上還是調(diào)用了父類(SymfonyComponentConsoleApplication)的 add

public function add(Command $command)
{
    ......

    $this->commands[$command->getName()] = $command;

    ......

    return $command;
}

關(guān)鍵在 $this->commands[$command->getName()] = $command;,參數(shù) $command 已經(jīng)知道是一個(gè) ServeCommand 對(duì)象,所以這一句的作用就是在 Artisan 對(duì)象的 $commands 屬性添加了一個(gè)鍵為 serve 、值為 ServeCommand 對(duì)象的成員。

getArtisan 方法執(zhí)行完后就會(huì)調(diào)用其返回的 Artisan 對(duì)象的 run 方法:

public function run(InputInterface $input = null, OutputInterface $output = null)
{
    $commandName = $this->getCommandName(
        $input = $input ?: new ArgvInput
    );

    $this->events->fire(
        new EventsCommandStarting(
            $commandName, $input, $output = $output ?: new ConsoleOutput
        )
    );

    $exitCode = parent::run($input, $output);

    $this->events->fire(
        new EventsCommandFinished($commandName, $input, $output, $exitCode)
    );

    return $exitCode;
}

$input 參數(shù)是在 artisan 腳本里 new 出來(lái)的 SymfonyComponentConsoleInputArgvInput 對(duì)象,getCommandName 是繼承自父類的方法:

protected function getCommandName(InputInterface $input)
{
    return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument();
}

也就是說(shuō)這個(gè)方法的返回結(jié)果就是 SymfonyComponentConsoleInputArgvInput 對(duì)象的 getFirstArgument 方法的返回值:

public function __construct(array $argv = null, InputDefinition $definition = null)
{
    if (null === $argv) {
        $argv = $_SERVER["argv"];
    }

    // strip the application name
    array_shift($argv);

    $this->tokens = $argv;

    parent::__construct($definition);
}

......

public function getFirstArgument()
{
    foreach ($this->tokens as $token) {
        if ($token && "-" === $token[0]) {
            continue;
        }

        return $token;
    }
}

getFirstArgument 方法會(huì)將屬性 $tokens 里第一個(gè)不包含 "-" 的成員返回,而 $tokens 屬性的值是在構(gòu)造函數(shù)里生成的,所以可以知道 getCommandName 的結(jié)果就是 serve 。

接下來(lái) Artisan 對(duì)象調(diào)用了父類的 run 方法(篇幅太長(zhǎng),省略掉一點(diǎn)):

public function run(InputInterface $input = null, OutputInterface $output = null)
{
    ......

    try {
        $exitCode = $this->doRun($input, $output);
    } catch (Exception $e) {
        if (!$this->catchExceptions) {
            throw $e;
    ......
}

public function doRun(InputInterface $input, OutputInterface $output)
{
    ......

    $name = $this->getCommandName($input);
    
    ......

    try {
        $e = $this->runningCommand = null;
        // the command name MUST be the first element of the input
        $command = $this->find($name);
    
    ......

    $this->runningCommand = $command;
    $exitCode = $this->doRunCommand($command, $input, $output);
    $this->runningCommand = null;

    return $exitCode;
}

protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
{
    ......

    if (null === $this->dispatcher) {
        return $command->run($input, $output);
    }

    ......
}

run 方法又會(huì)調(diào)用 doRun,而該方法會(huì)先使用 getCommandName 獲取到命令的名字("serve"),然后使用 find 方法找出與該命令對(duì)應(yīng)的 Command 對(duì)象(在 $commands 屬性中查找,該屬性的結(jié)構(gòu)類似 "serve" => "ServeCommand"),被找出來(lái)的 Command 對(duì)象會(huì)被作為參數(shù)傳遞到 doRunCommand 方法,最后在其中調(diào)用該對(duì)象的 run 方法(ServeCommand 沒(méi)有實(shí)現(xiàn)該方法,所以其實(shí)是調(diào)用父類 IlluminateConsoleCommandrun,但父類的方法實(shí)際也只有一行,那就是調(diào)用其父類的 run,所以貼出來(lái)的其實(shí)是 SymfonyComponentConsoleCommandCommandrun):

public function run(InputInterface $input, OutputInterface $output)
{
    ......

    if ($this->code) {
        $statusCode = call_user_func($this->code, $input, $output);
    } else {
        $statusCode = $this->execute($input, $output);
    }

    return is_numeric($statusCode) ? (int) $statusCode : 0;
}

$code 并沒(méi)有賦值過(guò),所以執(zhí)行的是 $this->execute($input, $output);ServeCommand 沒(méi)有實(shí)現(xiàn)該方法,IlluminateConsoleCommandexecute 方法如下:

protected function execute(InputInterface $input, OutputInterface $output)
{
    return $this->laravel->call([$this, "handle"]);
}

也就是調(diào)用了 ServeCommandhandle 方法:

public function handle()
{
    chdir($this->laravel->publicPath());

    $this->line("Laravel development server started: host()}:{$this->port()}>");

    passthru($this->serverCommand());
}

protected function serverCommand()
{
    return sprintf("%s -S %s:%s %s/server.php",
        ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false)),
        $this->host(),
        $this->port(),
        ProcessUtils::escapeArgument($this->laravel->basePath())
    );
}

所以如果想打開(kāi)一個(gè)簡(jiǎn)易的服務(wù)器做開(kāi)發(fā),把目錄切換到根目錄的 public 目錄下,敲一下這個(gè)命令,效果是差不多的, php -S 127.0.0.1:8000 ../server.php。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/28181.html

相關(guān)文章

  • Laravel Artisan 命令

    摘要:顯示幫助信息強(qiáng)制輸出禁用輸出 Laravel Framework version 5.1.3 (LTS) Usage: command [options] [arguments] Options: -h, --help 顯示幫助信息 -q, --quiet Do not output any message -V, --ve...

    txgcwm 評(píng)論0 收藏0
  • Vue報(bào)錯(cuò)SyntaxError:TypeError:this.getOptionsisnotafunction的解決方法

      一、簡(jiǎn)單介紹  Vue 開(kāi)發(fā)中會(huì)出現(xiàn)一些問(wèn)題,比如:Vue報(bào)錯(cuò)SyntaxError:TypeError:this.getOptionsisnotafunction,要如何解決?  二、報(bào)錯(cuò)現(xiàn)象  ERROR Failed to compile with 1 error 上午10:39:05  error in ./src/views/Login.vue?vue&type=style&...

    3403771864 評(píng)論0 收藏0
  • Laravel 5.4 入門(mén)系列 1. 安裝

    摘要:的安裝與使用是什么是的一個(gè)依賴管理工具。它以項(xiàng)目為單位進(jìn)行管理,你只需要聲明項(xiàng)目所依賴的代碼庫(kù),會(huì)自動(dòng)幫你安裝這些代碼庫(kù)。 Composer 的安裝與使用 Composer 是什么 Composer 是 PHP 的一個(gè)依賴管理工具。它以項(xiàng)目為單位進(jìn)行管理,你只需要聲明項(xiàng)目所依賴的代碼庫(kù),Composer 會(huì)自動(dòng)幫你安裝這些代碼庫(kù)。 安裝 Composer Mac 下的安裝只需要在命令行...

    hqman 評(píng)論0 收藏0
  • 源碼解讀:php artisan serve

    摘要:原文來(lái)自在學(xué)習(xí)的時(shí)候,可能很多人接觸的第一個(gè)的命令就是,這樣我們就可以跑起第一個(gè)的應(yīng)用。本文來(lái)嘗試解讀一下這個(gè)命令行的源碼。 原文來(lái)自:https://www.codecasts.com/blo... 在學(xué)習(xí) Laravel 的時(shí)候,可能很多人接觸的第一個(gè) artisan 的命令就是:php artisan serve,這樣我們就可以跑起第一個(gè) Laravel 的應(yīng)用。本文來(lái)嘗試解讀一...

    Loong_T 評(píng)論0 收藏0
  • 降低vue-router版本的2種解決方法實(shí)例

      在Vue.js官方的路由插件中,vue-router和vue.js是深度集成的,這類頁(yè)面適合用于構(gòu)建單頁(yè)面應(yīng)用。但要注意是由于無(wú)法注明版本,一般就默認(rèn)安裝router4.X,但我們創(chuàng)建的是vue2,只能結(jié)合 vue-router 3.x 版本才能使用。現(xiàn)在需要降低版本?! 》椒ā ∥覀冎纕ue-router 4.x 只能結(jié)合 vue3 進(jìn)行使用,vue-router 3.x 只能結(jié)合 vue...

    3403771864 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<