Laravel 生命周期
Laravel 的生命周期主要分为四个阶段: 加载依赖 创建应用实例 接收请求并响应 请求结束进行回调 这四个阶段都在 index.php 中完成: 1234567891011121314151617181920<?php// 加载依赖require __DIR__.'/../vendor/autoload.php';// 创建应用实例$app = require_once __DIR__.'/../bootstrap/app.php';// 实例化 HTTP 内核$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);// 接收请求,生成响应$response = $kernel->handle( $request = Illuminate\Http\Request::capture());// 发送响应$response->send();// 请求结束,进行回调$kernel->terminate($request, $response); 1. 加载依赖laravel 框架依赖 composer 管理扩展包,通过引入 composer 的自动加载程序,就可以轻松完成扩展加载: 1require __DIR__.'/../vendor/autoload.php'; 2 创建应用实例这一步主要由以下几个小步骤组成: 创建应用实例 完成基础注册 基础绑定 基础服务提供者注册 event log route 核心类别名注册 绑定核心 创建应用实例,由 bootstrap/app.php 完成,然后注册三个核心。 1234567<?php// 第一部分: 创建应用实例$app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../'));…… 2.1 完成基础注册应用实例创建后,再来看一下具体是怎么工作的,打开 Illuminate\Foundation\Application,其代码如下: 1234567891011121314public function __construct($basePath = null){ // 应用的路径绑定 if ($basePath) { $this->setBasePath($basePath); } // 将基础绑定注册到容器中,容器名 $this->registerBaseBindings(); // 将基础服务提供者注册到容器 Event、Log、Route $this->registerBaseServiceProviders(); // 将核心类别名注册到容器 $this->registerCoreContainerAliases();} 2.2 内核绑定接着看 bootstrap/app.php 中的代码: 12345678910111213141516171819……// 第二步,内核绑定$app->singleton( Illuminate\Contracts\Http\Kernel::class, App\Http\Kernel::class);$app->singleton( Illuminate\Contracts\Console\Kernel::class, App\Console\Kernel::class);$app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\Handler::class); 绑定三个内核,HTTP、Console、Exception内核。 3 接收请求并响应再次回到 index.php,查看请求和响应的相关代码: 1234567891011……$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);$response = $kernel->handle( $request = Illuminate\Http\Request::capture());$response->send();…… 这一步也是由几个小步骤组成: 实例化 HTTP 核心 实例化内核 注册中间件到路由 session 共享错误 身份验证请求 …… 请求处理 创建请求实例 处理请求,返回响应 发送响应 3.1 注册中间件到路由在 Illuminate\Contracts\Http\Kernel::class 类的构造方法中,将在 HTTP 内核定义的「中间件」注册到路由,注册完后就可以在实际处理 HTTP 请求前调用这些「中间件」实现过滤请求的目的。 1234567891011121314151617181920212223242526272829303132protected $middlewarePriority = [ \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Auth\Middleware\Authorize::class,];public function __construct(Application $app, Router $router){ $this->app = $app; $this->router = $router; $this->syncMiddlewareToRouter();}// 注册中间件到路由protected function syncMiddlewareToRouter(){ $this->router->middlewarePriority = $this->middlewarePriority; foreach ($this->middlewareGroups as $key => $middleware) { $this->router->middlewareGroup($key, $middleware); } foreach ($this->routeMiddleware as $key => $middleware) { $this->router->aliasMiddleware($key, $middleware); }} 3.2 处理请求处理请求实际包含两个阶段: 创建请求实例 处理请求 3.2.1 创建请求实例通过 Symfony 实例创建一个 Laravel 请求实例。 12345678910111213141516171819202122public static function capture(){ static::enableHttpMethodParameterOverride(); return static::createFromBase(SymfonyRequest::createFromGlobals());}public static function createFromBase(SymfonyRequest $request){ $newRequest = (new static)->duplicate( $request->query->all(), $request->request->all(), $request->attributes->all(), $request->cookies->all(), $request->files->all(), $request->server->all() ); $newRequest->headers->replace($request->headers->all()); $newRequest->content = $request->content; $newRequest->request = $newRequest->getInputSource(); return $newRequest;} 3.2.2 处理请求在 HTTP 核心的 handdle 方法内,接收一个请求,也就是上一步创建的请求实例,最终生成一个响应。 主要步驟如下: 注册请求到容器 运行引导程序 环境检测,将 env 中的配置读取到变量中 配置文件加载 加载异常处理 注册门面 注册服务提供者 服务启动 发送请求到路由 查找路由 运行控制器或匿名函数 返回响应 HTTP 核心的 handle 方法: 123456789101112131415161718public function handle($request){ try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Throwable $e) { $this->reportException($e); $response = $this->renderException($request, $e); } $this->app['events']->dispatch( new RequestHandled($request, $response) ); return $response;} 再往下深入,查看 $response = $this->sendRequestThroughRouter($request); 的具体实现: 12345678910111213141516protected function sendRequestThroughRouter($request){ // 将请求注册到容器 $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); // 启动引导程序 $this->bootstrap(); // 发送请求至路由 return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter());} 首先,将 request 注册到容器内,然后清除掉之前的 request 实例缓存,启动引导程序,然后将请求发送到路由。 接下来,看一下引导程序是做什么的: 12345678910111213141516171819202122232425262728293031323334protected $bootstrappers = [ \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class, \Illuminate\Foundation\Bootstrap\LoadConfiguration::class, \Illuminate\Foundation\Bootstrap\HandleExceptions::class, \Illuminate\Foundation\Bootstrap\RegisterFacades::class, \Illuminate\Foundation\Bootstrap\RegisterProviders::class, \Illuminate\Foundation\Bootstrap\BootProviders::class,];public function bootstrap(){ if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); }}protected function bootstrappers(){ return $this->bootstrappers;}// src/Illuminate/Foundation/Application.phppublic function bootstrapWith(array $bootstrappers){ $this->hasBeenBootstrapped = true; foreach ($bootstrappers as $bootstrapper) { $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]); $this->make($bootstrapper)->bootstrap($this); $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]); }} 在容器内的具体实现方法中,会先解析引导程序,然后再通过调用引导程序的 bootstrap 方法来启动服务。引导程序功能: 环境检测,将 env 配置文件载入到 $_ENV 变量中 加载配置文件 加载异常处理 加载 Facades,注册完成后可以用别名的方式访问具体的类 注册服务提供者,在这里我们会将配置在 app.php 文件夹下 providers 节点的服务器提供者注册到 APP 容器,供请求处理阶段使用 服务启动 在发送请求至路由这行代码中,完成了:管道(pipeline)创建、将 request 传入管道、对 request 执行中间件处理和实际的请求处理四个不同的操作。 1234return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); 继续深入 $this->dispatchToRouter(),分析程序是如何处理请求的: 注册请求 查找路由 运行控制器 返回响应结果 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364protected function dispatchToRouter(){ return function ($request) { // 将请求注册到容器 $this->app->instance('request', $request); return $this->router->dispatch($request); };}public function dispatch(Request $request){ $this->currentRequest = $request; return $this->dispatchToRoute($request);}public function dispatchToRoute(Request $request){ return $this->runRoute($request, $this->findRoute($request));}// 查找路由protected function findRoute($request){ $this->current = $route = $this->routes->match($request); $this->container->instance(Route::class, $route); return $route;}protected function runRoute(Request $request, Route $route){ $request->setRouteResolver(function () use ($route) { return $route; }); $this->events->dispatch(new RouteMatched($route, $request)); return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) );}protected function runRouteWithinStack(Route $route, Request $request){ $shouldSkipMiddleware = $this->container->bound('middleware.disable') && $this->container->make('middleware.disable') === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); // 返回运行结果 return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { // 运行匹配到的路由控制器或匿名函数 return $this->prepareResponse( $request, $route->run() ); });} 执行 $route->run() 的方法定义在 Illuminate\Routing\Route 类中: 1234567891011121314public function run(){ $this->container = $this->container ?: new Container; try { if ($this->isControllerAction()) { return $this->runController(); } return $this->runCallable(); } catch (HttpResponseException $e) { return $e->getResponse(); }} 如果路由的实现是一个控制器,会完成控制器实例化并执行指定方法;如果是一个匿名函数就会直接调用。最终响应通过 prepareResponse 返回。 3.2.3 发送响应绕了一大圈,最后终于回到了开始的地方 12// 发送响应$response->send(); 最终发送,由 src/Illuminate/Http/Response.php 的父类 Symfony\Component\HttpFoundation\Response 完成: 12345678910111213public function send(){ $this->sendHeaders(); $this->sendContent(); if (\function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { static::closeOutputBuffers(0, true); } return $this;} 4 请求结束,进行回调1$kernel->terminate($request, $response); 继续往下看,核心的 terminate 方法: 123456public function terminate($request, $response){ $this->terminateMiddleware($request, $response); $this->app->terminate();} terminateMiddleware 中,进行终止中间件,$this->app->terminate() 终止程序。 总结创建应用实例,完成项目路径注册、基础服务注册、核心类别名注册,然后将 HTTP 和 Console, Exception 核心注册到容器。 然后再实例化内核,将中间件加载到路由,再将请求注册到容器,然后运行引导程序,进行环境检测、加载系统配置等系统环境配置。 然后进行中间件校验,通过校验后才会最终处理实际的控制器或匿名函数并生成响应。 最终,发送响应给用户,清理项目中的中间件,完成一个请求周期。
使用 laravel mix 编译资源
学习下如何在 laravel 框架中,用 laravel mix 编译前端资源。 使用本次操作的环境依然是 laradock,如果没用特殊说明,以后应该默认 laradock。 workspace 容器中,已经提前装好了 node 环境,而在项目根目录中,package.json 和 webpack.mix.js 也已经为我们预设好了,所以laravel 项目建好后,直接在根目录安装即可: 1npm install 在 webpack.mix.js 中,已经加载了两个默认的文件: 12mix.js('resources/js/app.js', 'public/js') .sass('resources/sass/app.scss', 'public/css'); 我们只需要把自己的资源文件,按照同样的格式写入进去,然后开始运行,就可以生成编译后的资源了。 虽然示例中只写了 sass 一种样式文件,但是其实可以支持常见的以及不常见的很多中格式,只需要调用对应的接口即可。而且还可以把多个资源文件合并成一个。 举个🌰: 123456mix.less('resources/assets/less/app.less', 'public/stylesheets/styles.css');mix.styles([ 'public/css/vendor/normalize.css', 'public/css/vendor/videojs.css'], 'public/css/all.css'); 运行12npm run devnpm run watch 在上一篇中,我们修改视图后使之生效的命令,其实就是通知 mix 开始工作的。 单独使用在 laravel 框架之外也是可以使用 mix 的,具体教程请参考 [ learnku ] 其实不难,挺简单的。😎
laravel 内置 vue 的使用
从 5.3 版本开始,用 Vue.js 作为默认 JavaScript 前端框架。 从刚接触 laravel 到现在已经又过去了四个版本,种种原因,还是一直没能用上 vue.js 来做开发,现在刚好因为公司项目用到了 vue,对 vue 有了一定的了解,所以顺便就研究下 vue 在 laravel 中的使用吧。 安装laravel操作均在 laradock 的环境中进行。进入 workspace 容器,执行以下命令安装 laravel 1composer create-project laravel/laravel study 配置mysqldocker-compose up -d nginx mysql phpmyadmin 启动容器配置 nginx、hosts 并重启 nginx进入 mysql 容器执行以下命令: 123456mysql -uroot -prootALTER USER root IDENTIFIED WITH mysql_native_password BY 'PASSWORD';exit;exit 访问 phpmyadmin: localhost:8080,host 填写 mysql,用户名密码均为 root。 配置laravel修改数据库信息,生成用户模块并安装前端脚手架: 1234567891011121314php artisan make:authphp artisan migratephp artisan make:seed UsersTableSeeder在 run 方法中添加用户信息:$user = new App\User;$user->name = 'wu';$user->email = 'yf-wu@qq.com';$user->password = Hash::make('111111');$user->save();再去 DatabaseSeeder 中打开 run 中的注释,接着往下执行:php artisan db:seednpm install 修改视图home.blade.php:vue 的组件在 resources/js/components,然后在 app.js 中注册。 12You are logged in!<example-component></example-component> 更新脚手架:npm run dev or npm run watch 再实验下例子来自:[ cxp1539 ] 视图组件: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657<template> <transition name="fade"> <div v-if="isShow" class="goTop" @click="goTop"> <span class="glyphicon glyphicon-menu-up"></span> </div> </transition></template><script>export default { data() { return { isShow: false } }, mounted() { const that = this $(window).scroll(function() { if ($(this).scrollTop() > 50) { that.isShow = true } else { that.isShow = false } }) }, methods: { goTop() { $('html,body').animate({ scrollTop: 0 }) } }}</script><style scoped lang="scss"> .fade-enter-active, .fade-leave-active { transition: opacity .5s; } .fade-enter, .fade-leave-to { opacity: 0; } .goTop { position: fixed; right: 36px; bottom: 50px; background: #FFF; width: 50px; height: 50px; line-height: 60px; text-align: center; border-radius: 2px; box-shadow: 0 4px 12px 0 rgba(7,17,27,.1); cursor: pointer; z-index: 1000; span { font-size: 20px; } }</style> app.js 注册: 1Vue.component('go-top', require('./components/GoTop.vue')); 在 app.blade.php 中引入组件: 1234<main class="py-4"> @yield('content')</main><go-top></go-top> 为了使页面更高,随便修改个样式使滚动条出现。 注意事项 每次修改组件后都需要重新运行一次 npm run dev,也可以用 watch-poll 监听。 进阶使用到了上一步已经可以完成一些基础的操作了,实际上,刚才得操作还用到了一个叫做 laravel-mix 的东西,在 [ LearnKu ] (laravel-china 社区)社区的文档中是这么介绍的: Laravel Mix 提供了简洁且可读性高的 API ,用于使用几个常见的 CSS 和 JavaScript 预处理器为应用定义 Webpack 构建步骤。可以通过简单链式调用来定义资源的编译。 Laravel Mix 是叠加于 webpack 上的一层干净的膜, 它让 webpack 百分之80的用例变得十分简单。 也就是说,laravel-mix 是用来简化 webpack 学习和开发成本的工具。 对于后端人员来说,前端东西真的太多太难,mix 可以让我们不需要关注 webpack 的配置,即可轻松的编译前端脚本。 之前因为没在框架中用过 vue,所以一直也没有接触到这个工具,现在看完发现,学习之路真的是永无止境… 😂
travis-ci 可持续集成测试
给博客添加了 travis ci 可持续集成,以后本地可以不装 node 环境了。 操作过程:github 账号 登陆 travis ci登陆后把博客项目的开关打开。 博客项目创建分支把博客项目,即 .io 的那个项目拉到本地,创建一个新的分支 hexo: 123456git checkout -b hexogit rm -rf * #删除仓库中的文件,可能本地的文件还存有,则需要使用 rm -rf 命令git commit -m "清空文件夹" #提交删除信息git push origin hexo:hexo #将删除的信息推送到远程仓库 把博客源码放入 hexo 分支的文件夹中,然后再新建 .travis.yml: 123456789101112131415161718192021222324252627282930313233343536373839404142434445# 指定语言环境language: node_js# 指定需要sudo权限sudo: required# 指定node_js版本node_js: - 7.9.0# 指定缓存模块,可选。缓存可加快编译速度。cache: directories: - node_modules# 指定博客源码分支,因人而异。hexo博客源码托管在独立repo则不用设置此项branches: only: - hexobefore_install: - npm install -g hexo-cli# Start: Build Lifecycleinstall: - npm install - npm install hexo-deployer-git --save# 执行清缓存,生成网页操作script: - hexo clean - hexo generate# 设置git提交名,邮箱;替换真实token到_config.yml文件,最后depoy部署after_script: - cd ./public - git init - git config user.name "M-finder" - git config user.email "m@m-finder.com" - git add . - git commit -m "Travis ci push" - git push --force --quiet "https://${travis_token}@${gh_repo}" master:masterenv: global: - gh_repo: github.com/M-finder/M-finder.github.io.git# End: Build LifeCycle github生成token在 setting - developer settings 中生成 token,勾选 repo 所有选项和 user 下的 email。 把生成的 token 填写的 travis ci 的设置中。 完成后,提交代码到分支测试下是否正常。 添加配置文件并推送到分支后,build 成功但是徽章现在还一直是 unknown。 添加测试文章后无法推送到 master。 徽章状态原因:master 没有 build推送失败原因:github 生成的 token 名称和配置文件中不一致。
phpstorm 配置 laradock xdebug
本次操作为 win10 系统,理论上和其他系统无差异。 克隆 laradock 到本地:git clone https://github.com/Laradock/laradock.git 进入 laradock 文件夹, 生成配置文件:cp .\env-example .env 编辑 .env 配置文件: 1234WORKSPACE_INSTALL_XDEBUG=truePHP_FPM_INSTALL_XDEBUG=true 修改 laradock/php-fpm/xdebug.ini 和 laradock/workspace/xdebug.ini配置文件: 1234567891011121314151617xdebug.remote_host=dockerhostxdebug.remote_connect_back=0xdebug.remote_port=9000xdebug.idekey=PHPSTORMxdebug.remote_autostart=1xdebug.remote_enable=1xdebug.cli_color=0xdebug.profiler_enable=0xdebug.profiler_output_dir="~/xdebug/phpstorm/tmp/profiling"xdebug.remote_handler=dbgpxdebug.remote_mode=reqxdebug.var_display_max_children=-1xdebug.var_display_max_data=-1xdebug.var_display_max_depth=-1 然后在 laradock 同级新建 www 文件夹并在文件夹下新建 index.php 文件。修改 laradock/nginx/sites/default.conf 配置: 1root /var/www; build 服务: docker-compose bild php-fpm workspace 启动服务:docker-compose up -d nginx mysql workspace 打开 phpstorm,添加 php 设置和 server: 添加 php 时,要把 additional 里的两个内容填好。options 内容如下图: 1-dxdebug.remote_host=docker.for.win.localhost -dxdebug.remote_enable=1 -dxdebug.remote_port=9000 -dxdebug.remote_mode=req 建好以后,再添加 remote_debug: 配置好以后,启动 debug,打开电话按钮,添加断点刷新页面。 注: 配置好 xdebug 后,如果需要安装 laravel,需要把 phpstorm 关掉。否则会拦截的 composer 的请求。具体表现为执行任何 composer 的命令都没有反应。
新年快乐
久不更博,是因为最近在学习,然后把笔记都写在了简书。 博客不会关,域名已续费。 会忙里抽闲继续折腾。 只可惜想学想做的东西都太多,有点分身无暇。 不说了,19 年也都进来这么久了,祝大家新年快乐吧。
常用工具合计
图片处理类工具 Ai转常用图片格式 图片转favicon 图片转favicon2 艺术字生成 开发工具 代码转换 ascii文字生成 leangoo-团队协作工具 我喜欢的几个博客 litten Makito’s Notebook 澳洲小哥原始技术 MARKSZのBlog 魚·后花园
im-a-test
I am a test… test for git push, because I losted an article… 好吧,能推送,就是丢了周末的一个文件,虽然它明明就躺在文件夹里 既然能推送,那我就再得瑟一下。 大舅二舅都是他舅,大哈二哈都是条狗~ 噢噢 都是狗~~
Happy Birthday
昨天我爹生日,我竟然可耻的忘了…… 晚上看到我姑姑们在群里发红包祝福,吓得我赶紧打电话表示慰问。 结果接通电话我妈也忘了。 心疼我爹。 Happy Birthday!
Hello World
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new "My New Post" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment
