zoukankan      html  css  js  c++  java
  • laravel底层源码解析:pipeline,db,console

    参考1:https://www.jianshu.com/p/1e03ed5d8a66 (laravel源码概述目录)

    参考2:https://blog.csdn.net/qq_42611547/article/details/86521628(laravel所有底层源码解析链接)

    管道模式的精妙思维

    核心代码:

    public function then(Closure $destination)
        {
            $pipeline = array_reduce(
                array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
            );
            return $pipeline($this->passable);
        }
    
    protected function carry()
        {
            return function ($stack, $pipe) {
                return function ($passable) use ($stack, $pipe) {
                    if (is_callable($pipe)) {
                        // If the pipe is an instance of a Closure, we will just call it directly but
                        // otherwise we'll resolve the pipes out of the container and call it with
                        // the appropriate method and arguments, returning the results back out.
                        return $pipe($passable, $stack);
                    } elseif (! is_object($pipe)) {
                        [$name, $parameters] = $this->parsePipeString($pipe);
                        // If the pipe is a string we will parse the string and resolve the class out
                        // of the dependency injection container. We can then build a callable and
                        // execute the pipe function giving in the parameters that are required.
                        $pipe = $this->getContainer()->make($name);
                        $parameters = array_merge([$passable, $stack], $parameters);
                    } else {
                        // If the pipe is already an object we'll just make a callable and pass it to
                        // the pipe as-is. There is no need to do any extra parsing and formatting
                        // since the object we're given was already a fully instantiated object.
                        $parameters = [$passable, $stack];
                    }
    
                    $response = method_exists($pipe, $this->method)
                                    ? $pipe->{$this->method}(...$parameters)
                                    : $pipe(...$parameters);
                    return $response instanceof Responsable
                                ? $response->toResponse($this->getContainer()->make(Request::class))
                                : $response;
                };
            };
        }

    理解:

    基本过程
    array_reduce(),将所有 回调类型的pipe变成一个 回调调用栈
    passport介质传入调用栈,执行并返回
    
    最关键的思想
    整个过程都是传入匿名函数返回匿名函数,每一次调用栈都不是直接执行而是准备执行,这是。
    最后执行整个调用栈: return $pipeline($this->passable);
    
    对stack和pipe的理解:
    stack都是pipe是可执行单元,如果直观的把调用过程展示出来将是:由2种(array_reduce的callback,pipe)调用栈 间接递归调用的过程

    对函数的深入理解:
    1,函数可以是参数,返回值,可以接受参数从而构成的一个执行栈。从这个意义上一个进程可以看成一个巨大的函数。
    2,函数分为定义和执行2部分,定义上函数可以是匿名和非匿名的。
    3,看代码需要看函数是在定义还是在执行,什么时候定义和什么时候执行的

    Database源码基本方法

    DB代码解析:

    $user = DB::table('users')->where('name', 'John')->first();
    核心逻辑:
    1,DB是DatabaseManager类的实例,通过__call()代理成Connection和QueryBuilder这两个类上 2,pdo对象挂在Connection的属性上,connection对象和builder对象相互挂载 3,执行的最终环节在connection,对象的run()方法上
    4,curd语法编译环节在builder对象的各个方法上

    Model代码解析:

    基本逻辑

    $flight = AppFlight::where('number', 'FR 900')->first();//和车轮的分装一样,如果前面的方法调用了query的方法返回的就不是model实例而是pdo的执行结果的封装
    
    核心逻辑
    1,Model继承与EloquentModel,通过__call()和__callStatic()方法代理到EloquentBuilder上,而且需要先静态调用才能动态调用,因为前者可以实例化一个Model对象才能使用动态调用的方法;
    2,同样通过__call()方法代理到QueryBuilder上;这里有一个细节,__call返回的是this,不是qBuilder执行方法的结果。所以Model::first()能返回Model的实例
    3,QueryBuilder对象是从connecttion对象上获取的,并且connection对象是在boot方法里面设置的DB对象,非常巧妙;
    4,可以在connection属性上指定db配置里面的某个连接
    5,查询返回的是一个Model实例或实例集合:$builder->getModels($columns),
    DB,connection,pdo,qBuilder,Model,mBuilder有机的结合在一起了

    关于关联curd的底层逻辑

    参考:https://learnku.com/docs/laravel/6.x/eloquent-relationships/5177#012e7e (预加载解决N+1的问题)

    关联模型部分的源码学习成本性价比不高,封装的过于复杂,最终的操作还不如直接分步骤实现来的简洁易懂!

    Schema代码解析

    Schema::create('users', function (Blueprint $table) {
        $table->bigIncrements('id');
    });
    
    #核心类
    static::$app['db']->connection()->getSchemaBuilder();
    SchemaBuilder 
    SchemaBlueprint
    
    #核心代码
    public function build(Connection $connection, Grammar $grammar)
    {
        foreach ($this->toSql($connection, $grammar) as $statement) {
            $connection->statement($statement);
        }
    } 

    读写分离

    1,读写分离的逻辑:在db配置文件上配置read字段 后会自动实现
    2,也可以在DB类的connection()方法上使用指定配置的连接

    Artisan脚本命令服务

    核心代码:

    require __DIR__.'/vendor/autoload.php';
    $app = require_once __DIR__.'/bootstrap/app.php';
    
    $kernel = $app->make(IlluminateContractsConsoleKernel::class);
    $status = $kernel->handle(
        $input = new SymfonyComponentConsoleInputArgvInput,
        new SymfonyComponentConsoleOutputConsoleOutput
    );
    exit($status);

    注册调用逻辑:

    IlluminateFoundationConsoleKernel->handle($input, $output = null)
    
    IlluminateConsoleApplication->run($input, $output);
    
    SymfonyComponentConsoleApplication->run($input, $output);
    
    SymfonyComponentConsoleApplication->doRun($input, $output);
    
    function doRun(){
        $name = $this->getCommandName($input);
        $command = $this->find($name);
        $exitCode = $this->doRunCommand($command, $input, $output);
    }
    //相当于路由校验
    SymfonyComponentConsoleApplication->find($name);
    
    IlluminateConsoleCommand->run($input, $output);
    IlluminateConsoleCommand->execute($input, $output);
    
    public function find($name)
        {
            $this->init();
    
            $aliases = [];
    
            foreach ($this->commands as $command) {
                foreach ($command->getAliases() as $alias) {
                    if (!$this->has($alias)) {
                        $this->commands[$alias] = $command;
                    }
                }
            }
    
            $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
            $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
            $commands = preg_grep('{^'.$expr.'}', $allCommands);
    
            if (empty($commands)) {
                $commands = preg_grep('{^'.$expr.'}i', $allCommands);
            }
    
            $exact = in_array($name, $commands, true) || isset($aliases[$name]);
            return $this->get($exact ? $name : reset($commands));
        }

    队列:

    执行队列:
    php artisan queue:work #是一个无线循环的脚本进程

    定时任务调度:

    配置crontab
    * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

    命令行:

    //创建一个命令
    php artisan make:command SendEmails --command=sendemail
    //注册一个命令:上述命令会自动注册
    
    //执行一个命令
    php artisan sendemail

    //查看所有命令
    php artisan list
    php artisan help command-name
  • 相关阅读:
    Java基础类库
    Java工具类之浮点精确计算
    liunx安装telnet
    java代码中执行liunx命令
    Java 7 新的 try-with-resources 语句,自动资源释放
    mysql中单表多timestamp设置default问题
    Linux top命令的用法详细详解
    JVM调优之jstack找出最耗cpu的线程并定位代码
    每天一个liunx命令10之nohup和xargs
    每天一个liunx命令10之nohup和xargs
  • 原文地址:https://www.cnblogs.com/tkzc2013/p/12156061.html
Copyright © 2011-2022 走看看