laravel底层源码解析:pipeline,db,console
参考1:https://www.jianshu.com/p/1e03ed5d8a66 (laravel源码概述目录)
参考2:https://blog.csdn.net/qq_42611547/article/details/86521628(laravel所有底层源码解析链接)
参考3:http://www.edbiji.com/doccenter/showdoc/209/nav/3494.html(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和Query\Builder这两个类上 2,pdo对象挂在Connection的属性上,connection对象和builder对象相互挂载 3,执行的最终环节在connection,对象的run()方法上
4,curd语法编译环节在builder对象的各个方法上
Model代码解析:
基本逻辑
$flight = App\Flight::where('number', 'FR 900')->first();//和车轮的分装一样,如果前面的方法调用了query的方法返回的就不是model实例而是pdo的执行结果的封装 核心逻辑 1,Model继承与Eloquent\Model,通过__call()和__callStatic()方法代理到Eloquent\Builder上,而且需要先静态调用才能动态调用,因为前者可以实例化一个Model对象才能使用动态调用的方法; 2,同样通过__call()方法代理到Query\Builder上;这里有一个细节,__call返回的是this,不是qBuilder执行方法的结果。所以Model::first()能返回Model的实例 3,Query\Builder对象是从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(); Schema\Builder Schema\Blueprint #核心代码 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(Illuminate\Contracts\Console\Kernel::class); $status = $kernel->handle( $input = new Symfony\Component\Console\Input\ArgvInput, new Symfony\Component\Console\Output\ConsoleOutput ); exit($status);
注册调用逻辑:
Illuminate\Foundation\Console\Kernel->handle($input, $output = null) Illuminate\Console\Application->run($input, $output); Symfony\Component\Console\Application->run($input, $output); Symfony\Component\Console\Application->doRun($input, $output); function doRun(){ $name = $this->getCommandName($input); $command = $this->find($name); $exitCode = $this->doRunCommand($command, $input, $output); } //相当于路由校验 Symfony\Component\Console\Application->find($name); Illuminate\Console\Command->run($input, $output); Illuminate\Console\Command->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