<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Laravel - 她和她的猫</title>
    <link>https://her-cat.com/tags/laravel/</link>
    <description>Laravel的文章列表 - 她和她的猫</description>
    <image>
      <title>她和她的猫</title>
      <url>https://her-cat.com/assets/favorite.jpeg</url>
      <link>https://her-cat.com/assets/favorite.jpeg</link>
    </image>
    <generator>Hugo -- 0.148.1</generator>
    <language>zh</language>
    <lastBuildDate>Sun, 08 Jun 2025 11:58:52 +0800</lastBuildDate>
    <atom:link href="https://her-cat.com/tags/laravel/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Laravel 的 Facades 实现原理</title>
      <link>https://her-cat.com/posts/2019/12/29/the-realization-principle-of-laravel-facades/</link>
      <pubDate>Sun, 29 Dec 2019 23:52:41 +0800</pubDate>
      <guid>https://her-cat.com/posts/2019/12/29/the-realization-principle-of-laravel-facades/</guid>
      <description>在使用 Laravel 框架进行开发项目的时候，`Facades` 是一个经常能用到的模块，比如在使用缓存(Cache)、日志(Log) 等组件</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>在使用 Laravel 框架进行开发项目的时候，<code>Facades</code> 是一个经常能用到的模块，比如在使用缓存(Cache)、日志(Log) 等组件的地方。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">Illuminate\Support\Facades\Cache</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$name</span> <span class="o">=</span> <span class="nx">Cache</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="k">use</span> <span class="nx">Illuminate\Support\Facades\Log</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">Log</span><span class="o">::</span><span class="na">info</span><span class="p">(</span><span class="s1">&#39;this is log content&#39;</span><span class="p">);</span>
</span></span></code></pre></div><p><code>Facades</code> 的主要优点就是不需要记住各个组件所在目录对应的的命名空间，因为 <code>Illuminate\Support\Facades\</code> 这一段都是固定的，变化的只是后面的组件名称。</p>
<p>早在刚接触 Laravel 的时候，就对 <code>Facades</code> 充满了疑惑，为什么要这样用，直接用组件真正的命名空间不行吗，代码追踪过去又没有组件实现代码，只有一个静态方法 <code>getFacadeAccessor</code> 返回了组件的名称(cache、log)，但是在使用的时候又能调用到组件真正的方法。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Cache</span> <span class="k">extends</span> <span class="nx">Facade</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * Get the registered name of the component.
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return string
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">getFacadeAccessor</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s1">&#39;cache&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>那么真相只能在父类 <code>Facade</code> 里面了。</p>
<p>但是鉴于平时工作都在搬砖，做一名快乐的 <code>CURD Boy</code>，秉承着<code>实用主义</code>及<code>又不是不能用</code>的理念，所以当时并没有去深究代码（实际上是因为太菜了看不懂）。</p>
<p>那为什么现在又开始研究怎么它实现的呢？是我变强了吗？不！是因为需要用上它了&hellip;(实用主义万岁)</p>
<p>当时在写一个基于 <a href="https://github.com/overtrue/socialite">overtrue/socialite</a> 实现的 <a href="https://github.com/her-cat/colourlife-oauth2">her-cat/colourlife-oauth2</a> 扩展包，为了兼容 Laravel 的 <code>Facades</code> 用法，不得不了解一下 <code>Facades</code> 的实现原理（其实仔细看两遍文档就能知道个大概了）。</p>
<h2 id="实现原理">实现原理</h2>
<p>言归正传，就拿 <code>Cache</code> 来举例子，<code>Facades\Cache</code> 的文件内容就在上面，Cache 继承了抽象类 <code>Facade</code> 并实现了 <code>getFacadeAccessor</code> 静态方法，该方法返回了 <code>Cache 组件</code>（就是真正的缓存类）在 Laravel 容器中注册的名称，也就是 <code>cache</code>，这样我们就能从容器中取出 <code>Cache 组件</code> 的实例对象了，整个 <code>Facades</code> 实现也就是从容器中取出实例对象，让实例对象执行被调用的方法。</p>
<p>vendor/laravel/framework/src/Illuminate/Foundation/Application.php：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * Register the core class aliases in the container.
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return void
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">registerCoreContainerAliases</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">foreach</span> <span class="p">([</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="s1">&#39;cache&#39;</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="nx">\Illuminate\Cache\CacheManager</span><span class="o">::</span><span class="na">class</span><span class="p">,</span> <span class="nx">\Illuminate\Contracts\Cache\Factory</span><span class="o">::</span><span class="na">class</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="p">]</span> <span class="k">as</span> <span class="nv">$key</span> <span class="o">=&gt;</span> <span class="nv">$aliases</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">foreach</span> <span class="p">(</span><span class="nv">$aliases</span> <span class="k">as</span> <span class="nv">$alias</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">alias</span><span class="p">(</span><span class="nv">$key</span><span class="p">,</span> <span class="nv">$alias</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><p>当我们运行 <code>Cache::get('name')</code> 的时候，会先触发父类 <code>Facade</code> 的魔术方法 <code>__callStatic</code>，因为 <code>Facade</code> 并没有 <code>get</code> 方法。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * Handle dynamic, static calls to the object.
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param  string  $method
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param  array   $args
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return mixed
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @throws \RuntimeException
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="fm">__callStatic</span><span class="p">(</span><span class="nv">$method</span><span class="p">,</span> <span class="nv">$args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$instance</span> <span class="o">=</span> <span class="k">static</span><span class="o">::</span><span class="na">getFacadeRoot</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="nv">$instance</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="k">new</span> <span class="nx">RuntimeException</span><span class="p">(</span><span class="s1">&#39;A facade root has not been set.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$instance</span><span class="o">-&gt;</span><span class="nv">$method</span><span class="p">(</span><span class="o">...</span><span class="nv">$args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><p><code>$method</code> 是被调用的方法的名称(这个时候的值是 <code>get</code>)，<code>$args</code> 就是参数数组（<code>array(1) { [0]=&gt; string(4) &quot;name&quot; }</code>），通过  <code>getFacadeRoot</code> 方法获取到 <code>Cache 组件</code> 的实例对象，然后以 <code>$args</code> 作为参数，调用实例的 <code>get</code> 方法，最后返回方法执行结果。</p>
<p>以上就是方法就是 <code>Facade</code> 的实现原理了，接下来再看看 <code>getFacadeRoot</code> 方法。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * Get the root object behind the facade.
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return mixed
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">getFacadeRoot</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">static</span><span class="o">::</span><span class="na">resolveFacadeInstance</span><span class="p">(</span><span class="k">static</span><span class="o">::</span><span class="na">getFacadeAccessor</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><p>这个方法本身没有什么代码，就是调用了 <code>resolveFacadeInstance</code> 方法，通过组件名称从 Laravel 容器中取出对应的实例对象，这个组件名称就是通过 <code>Facades\Cache</code> 类的 <code>getFacadeAccessor</code> 方法获取的，这里的返回值就是 <code>cache</code>。</p>
<p>接下来就到了最后一个方法：<code>resolveFacadeInstance</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * Resolve the facade root instance from the container.
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param  object|string  $name
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return mixed
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">resolveFacadeInstance</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">is_object</span><span class="p">(</span><span class="nv">$name</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nv">$name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">isset</span><span class="p">(</span><span class="k">static</span><span class="o">::</span><span class="nv">$resolvedInstance</span><span class="p">[</span><span class="nv">$name</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="k">static</span><span class="o">::</span><span class="nv">$resolvedInstance</span><span class="p">[</span><span class="nv">$name</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">static</span><span class="o">::</span><span class="nv">$app</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="k">static</span><span class="o">::</span><span class="nv">$resolvedInstance</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span> <span class="o">=</span> <span class="k">static</span><span class="o">::</span><span class="nv">$app</span><span class="p">[</span><span class="nv">$name</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><p>此时 <code>$name</code> 的值是 <code>cache</code>。如果 <code>$name</code> 是一个对象的话，就直接返回 <code>$name</code>，<code>$resolvedInstance</code> 是一个用来保存已解析过的实例对象的数组，判断 <code>$name</code> 是否已经从 Laravel 容器中解析过，如果已经解析过就直接返回。</p>
<blockquote>
<p>注意：这里是的判断是在这一次请求的生命周期内是否解析过，下次请求进来的时候还是会从 Laravel 容器中取出来。</p></blockquote>
<p>如果 Laravel 容器如果不是空的话，就通过 <code>$name</code> 从 Laravel 容器中取出 <code>Cache 组件</code> 的实例对象，将 <code>$name</code> 作为 key 存入解析过的实例对象的数组中，并返回。</p>
<h2 id="精简版实现及测试代码">精简版实现及测试代码</h2>
<p>好了，<code>Facades</code> 的实现代码到这里就完了，最后再附上精简版的 <code>Facades</code> 及测试代码。</p>
<p><code>Facade</code> 抽象类：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nx">App\Core</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">abstract</span> <span class="k">class</span> <span class="nc">Facade</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * 用于存放实例对象的数组（实际上在 Laravel 中并不是数组，而是 Application 对象）
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @var array
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">static</span> <span class="nv">$app</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * 用于保存解析过的实例对象的数组
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @var array
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">static</span> <span class="nv">$resolvedInstance</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * 获取 facade 绑定的实例对象
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return mixed
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">getFacadeRoot</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="k">static</span><span class="o">::</span><span class="na">resolveFacadeInstance</span><span class="p">(</span><span class="k">static</span><span class="o">::</span><span class="na">getFacadeAccessor</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * 获取组件在容器中注册的名称
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return string
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @throws \Exception
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">getFacadeAccessor</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">throw</span> <span class="k">new</span> <span class="nx">\Exception</span><span class="p">(</span><span class="s1">&#39;Facade does not implement getFacadeAccessor method.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * 从容器中获取组件的实例对象
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param  object|string  $name
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return mixed
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">protected</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">resolveFacadeInstance</span><span class="p">(</span><span class="nv">$name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">is_object</span><span class="p">(</span><span class="nv">$name</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nv">$name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">isset</span><span class="p">(</span><span class="k">static</span><span class="o">::</span><span class="nv">$resolvedInstance</span><span class="p">[</span><span class="nv">$name</span><span class="p">]))</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="k">static</span><span class="o">::</span><span class="nv">$resolvedInstance</span><span class="p">[</span><span class="nv">$name</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="k">static</span><span class="o">::</span><span class="nv">$app</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="k">static</span><span class="o">::</span><span class="nv">$resolvedInstance</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span> <span class="o">=</span> <span class="k">static</span><span class="o">::</span><span class="nv">$app</span><span class="p">[</span><span class="nv">$name</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * Handle dynamic, static calls to the object.
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param  string  $method
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param  array   $args
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return mixed
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @throws \Exception
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="fm">__callStatic</span><span class="p">(</span><span class="nv">$method</span><span class="p">,</span> <span class="nv">$args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$instance</span> <span class="o">=</span> <span class="k">static</span><span class="o">::</span><span class="na">getFacadeRoot</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span> <span class="nv">$instance</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">throw</span> <span class="k">new</span> <span class="nx">\Exception</span><span class="p">(</span><span class="s1">&#39;A facade root has not been set.&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$instance</span><span class="o">-&gt;</span><span class="nv">$method</span><span class="p">(</span><span class="o">...</span><span class="nv">$args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * 设置组件实例对象（Laravel源码并无该方法，为了演示 Facade 加上的）
</span></span></span><span class="line"><span class="cl"><span class="sd">     *
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param $name
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param $obj
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">setComponentInstance</span><span class="p">(</span><span class="nv">$name</span><span class="p">,</span> <span class="nv">$obj</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">self</span><span class="o">::</span><span class="nv">$app</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$obj</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>继承了 <code>Facade</code> 的类：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nx">App\Facades</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">use</span> <span class="nx">App\Core\Facade</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd"> * @method static mixed get($key)
</span></span></span><span class="line"><span class="cl"><span class="sd"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Cache</span> <span class="k">extends</span> <span class="nx">Facade</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">getFacadeAccessor</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s1">&#39;cache&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd"> * @method static mixed info($key)
</span></span></span><span class="line"><span class="cl"><span class="sd"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Log</span> <span class="k">extends</span> <span class="nx">Facade</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">getFacadeAccessor</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 这里与上面不一样，返回的是组件的实例
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">return</span> <span class="k">new</span> <span class="nx">\App\Components\Log</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>组件的实现：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nx">App\Components</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Cache</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">get</span><span class="p">(</span><span class="nv">$key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;key:</span><span class="si">{</span><span class="nv">$key</span><span class="si">}</span><span class="s2">, value: her-cat&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Log</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">function</span> <span class="nf">info</span><span class="p">(</span><span class="nv">$content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="s2">&#34;记录的内容:</span><span class="si">{</span><span class="nv">$content</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>测试代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">namespace</span> <span class="nx">App\Test</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 将 Cache 组件的实例对象存入 static::$app 中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">\App\Core\Facade</span><span class="o">::</span><span class="na">setComponentInstance</span><span class="p">(</span><span class="s1">&#39;cache&#39;</span><span class="p">,</span> <span class="k">new</span> <span class="nx">\App\Components\Cache</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">echo</span> <span class="nx">\App\Facades\Cache</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">&#39;name&#39;</span><span class="p">)</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Log 组件并未注册到 static::$app 中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">\App\Facades\Log</span><span class="o">::</span><span class="na">info</span><span class="p">(</span><span class="s1">&#39;哈哈哈哈&#39;</span><span class="p">)</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>在 Laravel 登录/注册中使用 mews/captcha 扩展包</title>
      <link>https://her-cat.com/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/</link>
      <pubDate>Fri, 10 Aug 2018 21:57:20 +0800</pubDate>
      <guid>https://her-cat.com/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/</guid>
      <description>在网站中，为了防止恶意通过数据字典撞库攻击、注册机批量注册账号，会使用一些防范措施，比如图片验证码、手机验证码、手势验证码、Geetest。今天就介绍一下如何使</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>在网站中，为了防止恶意通过数据字典撞库攻击、注册机批量注册账号，会使用一些防范措施，比如图片验证码、手机验证码、<a href="https://www.vaptcha.com/">手势验证码</a>、<a href="http://www.geetest.com">Geetest</a>。今天就介绍一下如何使用 mews/captcha 扩展包防止恶意注册。</p>
<h2 id="用户认证脚手架">用户认证脚手架</h2>
<p>Laravel 自带了 <a href="https://laravel-china.org/docs/laravel/5.6/authentication/1379">用户认证功能</a>，我们将使用此功能来快速构建登录注册。</p>
<p>首先执行生成用户认证脚手架命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ php artisan make:auth
</span></span></code></pre></div><p>在 <code>.env</code> 中配置数据库连接信息：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">DB_CONNECTION=mysql
</span></span><span class="line"><span class="cl">DB_HOST=127.0.0.1
</span></span><span class="line"><span class="cl">DB_PORT=3306
</span></span><span class="line"><span class="cl">DB_DATABASE=数据库名称
</span></span><span class="line"><span class="cl">DB_USERNAME=数据库用户名
</span></span><span class="line"><span class="cl">DB_PASSWORD=数据库密码
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>执行迁移，创建用户认证相关表结构：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ php artisan migrate
</span></span></code></pre></div><p>运行结果：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ php artisan migrate
</span></span><span class="line"><span class="cl">Migration table created successfully.
</span></span><span class="line"><span class="cl">Migrating: 2014_10_12_000000_create_users_table
</span></span><span class="line"><span class="cl">Migrated:  2014_10_12_000000_create_users_table
</span></span><span class="line"><span class="cl">Migrating: 2014_10_12_100000_create_password_resets_table
</span></span><span class="line"><span class="cl">Migrated:  2014_10_12_100000_create_password_resets_table
</span></span></code></pre></div><p>这时候你打开数据库就能看到 <code>migrations</code>、<code>users</code> 两张表，<code>migrations</code> 是用来记录数据库迁移日志的，<code>users</code> 则是用户表，用于登录/注册。</p>
<p>手动在浏览器中打开 <code>http://127.0.0.1:8000/register</code>，就能看到注册界面了。</p>
<blockquote>
<p><code>http://127.0.0.1:8000</code> 使用的是 PHP 内置的服务器，可以使用 <code>php artisan serve</code> 命令来启动 PHP 服务器，你也可以使用自己的运行环境。</p></blockquote>
<p><img alt="注册页面" decoding="async" height="464" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-page.png" srcset="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-page_hu_2b001342c0734b24.png 384w, /posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-page_hu_955a6377201433e2.png 768w" style="max-width: 100%; height: auto; aspect-ratio: 2.1638;" width="1004"></p>
<h2 id="安装-captcha">安装 Captcha</h2>
<p>在 <a href="https://her-cat.com/posts/2018/08/09/use-mews-captcha-to-add-image-verification-codes-to-laravel-applications/">上一篇文章</a> 中已经介绍过如何安装 Captcha，这里就不赘述了。</p>
<h2 id="使用-captcha">使用 Captcha</h2>
<h3 id="修改注册视图文件">修改注册视图文件</h3>
<p>首先要在确认密码输入框下面加入一个验证码输入框及图片验证码。</p>
<p>打开 <code>resources/views/auth/register.blade.php</code> 视图文件，添加如下代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;div class=&#34;form-group{{ $errors-&gt;has(&#39;code&#39;) ? &#39; has-error&#39; : &#39;&#39; }}&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;label for=&#34;password-confirm&#34; class=&#34;col-md-4 control-label&#34;&gt;Verification Code&lt;/label&gt;
</span></span><span class="line"><span class="cl">    &lt;div class=&#34;col-md-3&#34;&gt;
</span></span><span class="line"><span class="cl">        &lt;input id=&#34;password-confirm&#34; type=&#34;text&#34; class=&#34;form-control&#34; name=&#34;code&#34; required&gt;
</span></span><span class="line"><span class="cl">        @if ($errors-&gt;has(&#39;code&#39;))
</span></span><span class="line"><span class="cl">            &lt;span class=&#34;help-block&#34;&gt;
</span></span><span class="line"><span class="cl">                &lt;strong&gt;{{ $errors-&gt;first(&#39;code&#39;) }}&lt;/strong&gt;
</span></span><span class="line"><span class="cl">            &lt;/span&gt;
</span></span><span class="line"><span class="cl">        @endif
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">    &lt;div class=&#34;col-md-3&#34;&gt;
</span></span><span class="line"><span class="cl">        &lt;img src=&#34;{{ captcha_src() }}&#34; alt=&#34;点击刷新&#34; onclick=&#34;this.src=&#39;{{ url(&#39;captcha/default&#39;) }}?s=&#39;+Math.random()&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">&lt;/div&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>刷新注册页面：</p>
<p><img alt="包含验证码的注册页面" decoding="async" height="535" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-page2.png" srcset="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-page2_hu_c7c37cc63773833a.png 384w, /posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-page2_hu_e51aa276656dc6d7.png 768w" style="max-width: 100%; height: auto; aspect-ratio: 1.8673;" width="999"></p>
<h3 id="修改注册控制器">修改注册控制器</h3>
<p>接下来就是在校验参数的方法中增加校验验证码的规则，打开 <code>app/Http/Controllers/Auth/RegisterController.php</code>，修改 <code>validator</code> 方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">protected function validator(array $data)
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    return Validator::make($data, [
</span></span><span class="line"><span class="cl">        &#39;name&#39; =&gt; &#39;required|string|max:255&#39;,
</span></span><span class="line"><span class="cl">        &#39;email&#39; =&gt; &#39;required|string|email|max:255|unique:users&#39;,
</span></span><span class="line"><span class="cl">        &#39;password&#39; =&gt; &#39;required|string|min:6|confirmed&#39;,
</span></span><span class="line"><span class="cl">        &#39;code&#39; =&gt; &#39;required|captcha&#39;,
</span></span><span class="line"><span class="cl">    ]);
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h2 id="验证-captcha">验证 Captcha</h2>
<p>故意输入错误的验证码：</p>
<p><img alt="错误的验证码" decoding="async" height="580" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/invalid-captcha.png" srcset="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/invalid-captcha_hu_4c217e5da3cd3974.png 384w, /posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/invalid-captcha_hu_fde156454608811d.png 768w" style="max-width: 100%; height: auto; aspect-ratio: 1.7500;" width="1015"></p>
<p>可以看到验证码的输入框变成了红色，但是提示的信息 <code>validation.captcha</code> 有点问题，并不是正确的提示语。</p>
<p>我们需要手动配置验证失败的提示语，打开 <code>resources/lang/en/validation.php</code> 文件，在 key 为 <code>url</code> 后下面一行添加提示语：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&#39;url&#39;                  =&gt; &#39;The :attribute format is invalid.&#39;,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&#39;captcha&#39;              =&gt; &#39;验证码错误。&#39;,
</span></span></code></pre></div><p>再次输入错误的验证码，就可以看到配置的提示语了：</p>
<p><img alt="错误的验证码" decoding="async" height="582" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/invalid-captcha-cn.png" srcset="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/invalid-captcha-cn_hu_78ce15ac517eeae1.png 384w, /posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/invalid-captcha-cn_hu_344db417bcb83f05.png 768w" style="max-width: 100%; height: auto; aspect-ratio: 1.7337;" width="1009"></p>
<p>最后我们按照规则输入正确的参数，就可以通过验证注册成功了：</p>
<p><img alt="注册成功" decoding="async" height="584" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-success.png" srcset="/posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-success_hu_ce672efccd76d20e.png 384w, /posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-success_hu_e88ce4ab9de40deb.png 768w, /posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-success_hu_56718d1d6201f9cf.png 1024w, /posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-success_hu_223aab56cf30d265.png 1536w, /posts/2018/08/10/use-of-mews-captcha-in-laravel-login-register/register-success.png 1546w" style="max-width: 100%; height: auto; aspect-ratio: 2.6473;" width="1546"></p>
]]></content:encoded>
    </item>
    <item>
      <title>使用 mews/captcha 扩展包为 Laravel 应用添加图片验证码</title>
      <link>https://her-cat.com/posts/2018/08/09/use-mews-captcha-to-add-image-verification-codes-to-laravel-applications/</link>
      <pubDate>Thu, 09 Aug 2018 23:55:38 +0800</pubDate>
      <guid>https://her-cat.com/posts/2018/08/09/use-mews-captcha-to-add-image-verification-codes-to-laravel-applications/</guid>
      <description>mews/captcha 是一个图片验证码扩展包，通过它我们能够快速的为 Laravel 增加验证码的功能。</description>
      <content:encoded><![CDATA[<h2 id="安装扩展包">安装扩展包</h2>
<p><a href="https://github.com/mewebstudio/captcha">mews/captcha</a> 是一个图片验证码扩展包，通过它我们能够快速的为 Laravel 增加验证码的功能。</p>
<p>使用 Composer 安装扩展包：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ composer require mews/captcha
</span></span></code></pre></div><blockquote>
<p>如果是在 Windows 环境中，需要在 php.ini 文件中取消 <code>php_gd2.dll</code>、<code>php_fileinfo.dll</code>、<code>php_mbstring.dll</code> 的注释，这些都是 mews/captcha 依赖的组件。</p></blockquote>
<p>打开 <code>config/app.php </code> 。</p>
<p>在 key 为 <code>providers</code> 的数组中注册 Captcha 服务提供者。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&#39;providers&#39; =&gt; [
</span></span><span class="line"><span class="cl">    // ...
</span></span><span class="line"><span class="cl">    Mews\Captcha\CaptchaServiceProvider::class,
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>在 key 为 <code>aliases</code> 的数组中注册 Captcha 别名。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&#39;aliases&#39; =&gt; [
</span></span><span class="line"><span class="cl">    // ...
</span></span><span class="line"><span class="cl">    &#39;Captcha&#39; =&gt; Mews\Captcha\Facades\Captcha::class,
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>生成配置文件 <code>config/captcha.php</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ php artisan vendor:publish --provider=&#34;Mews\Captcha\CaptchaServiceProvider&#34;
</span></span></code></pre></div><p>打开 <code>config/captcha.php</code> 文件，其中有四种图片验证码的配置，通过修改里面的参数来调整生成的验证码的规则，你可以通过新建一个 key 创建新的配置，这里我们使用的默认的配置(<code>default</code>)。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">return [
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    // 生成的验证码字符集
</span></span><span class="line"><span class="cl">    &#39;characters&#39; =&gt; &#39;2346789abcdefghjmnpqrtuxyzABCDEFGHJMNPQRTUXYZ&#39;,
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    &#39;default&#39;   =&gt; [
</span></span><span class="line"><span class="cl">        &#39;length&#39;    =&gt; 5,   // 验证码字符长度
</span></span><span class="line"><span class="cl">        &#39;width&#39;     =&gt; 120, // 图片长度
</span></span><span class="line"><span class="cl">        &#39;height&#39;    =&gt; 36,  // 图片高度
</span></span><span class="line"><span class="cl">        &#39;quality&#39;   =&gt; 90,  // 图片质量
</span></span><span class="line"><span class="cl">    ],
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    &#39;flat&#39;   =&gt; [...],
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    &#39;mini&#39;   =&gt; [...],
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    &#39;inverse&#39;   =&gt; [...]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">];
</span></span></code></pre></div><h2 id="小试牛刀">小试牛刀</h2>
<p>接下来我们用一个发布帖子的例子来展示如何在 Laravel 中使用 Captcha。</p>
<p>首先在 <code>routes/web.php</code> 文件中添加相关路由。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Route::get(&#39;posts/create&#39;, &#39;PostsController@showCreateForm&#39;)-&gt;name(&#39;posts.create&#39;);
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Route::post(&#39;posts&#39;, &#39;PostsController@store&#39;)-&gt;name(&#39;posts.store&#39;);
</span></span></code></pre></div><p>创建 <code>PostsController</code> 控制器。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$ php artisan make:controller PostsController
</span></span></code></pre></div><p>在 <code>PostsController</code> 中添加路由中对应的方法。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">/*
</span></span><span class="line"><span class="cl"> * 显示发布帖子的表单
</span></span><span class="line"><span class="cl"> */
</span></span><span class="line"><span class="cl">public function showCreateForm() 
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    return view(&#39;posts.create&#39;);
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">/*
</span></span><span class="line"><span class="cl"> * 保存一篇新的帖子
</span></span><span class="line"><span class="cl"> * @param Request $request
</span></span><span class="line"><span class="cl"> */
</span></span><span class="line"><span class="cl">public function store(Request $request)
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    // 验证并存储帖子...
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>创建视图文件 <code>resources/views/posts/create.blade.php</code>，其中 <code>@if ($errors-&gt;any())</code> 代码片段是用输出表单验证失败时的错误提示。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;!DOCTYPE html&gt;
</span></span><span class="line"><span class="cl">&lt;html lang=&#34;en&#34;&gt;
</span></span><span class="line"><span class="cl">&lt;head&gt;
</span></span><span class="line"><span class="cl">    &lt;meta charset=&#34;utf-8&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;title&gt;发布帖子&lt;/title&gt;
</span></span><span class="line"><span class="cl">&lt;/head&gt;
</span></span><span class="line"><span class="cl">&lt;body&gt;
</span></span><span class="line"><span class="cl">    @if ($errors-&gt;any())
</span></span><span class="line"><span class="cl">        &lt;ul style=&#34;color:red;&#34;&gt;
</span></span><span class="line"><span class="cl">            @foreach ($errors-&gt;all() as $error)
</span></span><span class="line"><span class="cl">                &lt;li&gt;{{ $error }}&lt;/li&gt;
</span></span><span class="line"><span class="cl">            @endforeach
</span></span><span class="line"><span class="cl">        &lt;/ul&gt;
</span></span><span class="line"><span class="cl">    @endif
</span></span><span class="line"><span class="cl">    &lt;form action=&#34;{{ route(&#39;posts.store&#39;) }}&#34; method=&#34;post&#34;&gt;
</span></span><span class="line"><span class="cl">        帖子标题：&lt;input type=&#34;text&#34; name=&#34;title&#34;&gt;  &lt;br/&gt;
</span></span><span class="line"><span class="cl">        帖子内容：&lt;input type=&#34;text&#34; name=&#34;content&#34;&gt; &lt;br/&gt;
</span></span><span class="line"><span class="cl">        验 证 码：&lt;input type=&#34;text&#34; name=&#34;code&#34;&gt; &lt;img src=&#34;{{ captcha_src() }}&#34; alt=&#34;点击刷新&#34; onclick=&#34;this.src=&#39;{{ url(&#39;captcha/default&#39;) }}?s=&#39;+Math.random()&#34;&gt; &lt;br/&gt;
</span></span><span class="line"><span class="cl">        &lt;input type=&#34;hidden&#34; name=&#34;_token&#34; value=&#34;{{ csrf_token() }}&#34;&gt;
</span></span><span class="line"><span class="cl">        &lt;input type=&#34;submit&#34; value=&#34;提交&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;/form&gt;
</span></span><span class="line"><span class="cl">&lt;/body&gt;
</span></span><span class="line"><span class="cl">&lt;/html&gt;
</span></span></code></pre></div><p>在浏览器中打开 <code>http://127.0.0.1:8000/posts/create</code> 就可以看到这个页面了，点击验证码可以刷新验证码。</p>
<blockquote>
<p><code>http://127.0.0.1:8000</code> 使用的是 PHP 内置的服务器，可以使用 <code>php artisan serve</code> 命令来启动 PHP 服务器，你也可以使用自己的运行环境。</p></blockquote>
<p><img alt="注册页面" decoding="async" height="221" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2018/08/09/use-mews-captcha-to-add-image-verification-codes-to-laravel-applications/create-post.png" srcset="/posts/2018/08/09/use-mews-captcha-to-add-image-verification-codes-to-laravel-applications/create-post_hu_50c6201a3dd1e570.png 384w" style="max-width: 100%; height: auto; aspect-ratio: 2.1765;" width="481"></p>
<p>接下来在 <code>store</code> 方法中编写验证表单的的逻辑。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">/**
</span></span><span class="line"><span class="cl">* 保存一篇新的帖子
</span></span><span class="line"><span class="cl">* @param Request $request
</span></span><span class="line"><span class="cl">*/
</span></span><span class="line"><span class="cl">public function store(Request $request)
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    // 验证表单的规则
</span></span><span class="line"><span class="cl">    $rules = [
</span></span><span class="line"><span class="cl">        &#39;title&#39; =&gt; &#39;required|string|max:255&#39;,
</span></span><span class="line"><span class="cl">        &#39;content&#39; =&gt; &#39;required|string|max:1000&#39;,
</span></span><span class="line"><span class="cl">        &#39;code&#39; =&gt; &#39;required|captcha&#39;,
</span></span><span class="line"><span class="cl">    ];
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    // 使用 rules 验证表单
</span></span><span class="line"><span class="cl">    $request-&gt;validate($rules);
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    // 验证成功则存入数据库
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    echo &#39;添加成功&#39;;
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><blockquote>
<p>这里用到了 Laravel 中的表单验证，不清楚的可以先看一下 <a href="https://laravel-china.org/docs/laravel/5.5/validation/1372">表单验证机制详解</a></p></blockquote>
<p><code>'code' =&gt; 'required|captcha'</code> 表示 <code>code</code> 这个参数是必需的，并使用 <code>captcha</code> 规则来验证该字段。然后使用 <code>Request</code> 对象中的 <code>validate</code> 方法对请求的参数进行校验。</p>
<p>如果验证失败则会返回到来源页面，并将验证失败的错误信息存到 <code>Session</code> 中，在页面上可以通过 <code>$errors-&gt;all()</code> 来获取错误信息。</p>
<p>如果我们什么都不填，直接提交表单，就会看到如下图的提示。</p>
<p><img alt="必填参数" decoding="async" height="317" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2018/08/09/use-mews-captcha-to-add-image-verification-codes-to-laravel-applications/required-params.png" srcset="/posts/2018/08/09/use-mews-captcha-to-add-image-verification-codes-to-laravel-applications/required-params_hu_981d5719ea38c04d.png 384w" style="max-width: 100%; height: auto; aspect-ratio: 1.6088;" width="510"></p>
<p>如果按照 <code>$rules</code> 的验证规则来填写表单就可以看到 <code>发布成功</code> ，表示参数都已经通过验证了。</p>
<h2 id="captcha-相关方法">Captcha 相关方法</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// 获取验证码图片
</span></span><span class="line"><span class="cl">captcha(); // Captcha::create();
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// 获取验证码图片地址（http://127.0.0.1:8000/captcha/default?Lh6ngrPi）
</span></span><span class="line"><span class="cl">captcha_src(); // Captcha::src();
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// 获取验证码 HTML (&lt;img src=&#34;http://127.0.0.1:8000/captcha/default?Lh6ngrPi&#34; &gt;)
</span></span><span class="line"><span class="cl">captcha_img(); // Captcha::img();
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// 使用其它配置
</span></span><span class="line"><span class="cl">captcha_src(&#39;flat&#39;); // Captcha::src(&#39;flat&#39;);
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>Laravel 授权策略（Policy）的基本使用</title>
      <link>https://her-cat.com/posts/2018/07/03/basic-usage-of-laravel-authorization-policy/</link>
      <pubDate>Tue, 03 Jul 2018 23:30:12 +0800</pubDate>
      <guid>https://her-cat.com/posts/2018/07/03/basic-usage-of-laravel-authorization-policy/</guid>
      <description>Policy（即策略）是在特定模型或者资源中组织授权逻辑的类，用来处理用户授权动作。比如在博客程序中会有一个 Article 模型，这个模型就会有一个相应的</description>
      <content:encoded><![CDATA[<p><code>Policy</code>（即策略）是在特定模型或者资源中组织授权逻辑的类，用来处理用户授权动作。</p>
<p>比如在博客程序中会有一个 <code>Article</code> 模型，这个模型就会有一个相应的 <code>ArticlePolicy</code> 来对用户的操作进行授权，比如在修改一篇文章时，我们会这样写：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$article</span> <span class="o">=</span> <span class="nx">Article</span><span class="o">::</span><span class="na">find</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 校验这篇是否属于当前用户
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="nv">$article</span><span class="o">-&gt;</span><span class="na">user_id</span> <span class="o">==</span> <span class="nx">Auth</span><span class="o">::</span><span class="na">id</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 修改文章
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="k">true</span><span class="p">;</span>
</span></span></code></pre></div><p><code>Policy</code> 其实就是将校验的逻辑从控制器转移到相对应的模型策略 （<code>ArticlePolicy</code>） 中。</p>
<h2 id="生成-policy">生成 Policy</h2>
<p>使用 <code>php artisan make:policy ArticlePolicy</code> 命令生成 Policy，保存在 <code>app/Policies</code> 目录下。</p>
<h2 id="注册-policy">注册 Policy</h2>
<p>然后在 <code>app/Providers/AuthServiceProvider.php</code> 的 <code>policies</code> 数组中注册该策略，将 <code>Article</code> 模型与对应的 <code>ArticlePolicy</code> 策略进行绑定。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">?</span><span class="n">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">namespace</span> <span class="n">App</span>\<span class="n">Providers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">App</span>\<span class="n">Models</span>\<span class="n">Article</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">App</span>\<span class="n">Policies</span>\<span class="n">ArticlePolicy</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">Illuminate</span>\<span class="n">Support</span>\<span class="n">Facades</span>\<span class="n">Gate</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">Illuminate</span>\<span class="n">Foundation</span>\<span class="n">Support</span>\<span class="n">Providers</span>\<span class="n">AuthServiceProvider</span> <span class="n">as</span> <span class="n">ServiceProvider</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="n">AuthServiceProvider</span> <span class="k">extends</span> <span class="n">ServiceProvider</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">/**</span>
</span></span><span class="line"><span class="cl">     <span class="o">*</span> <span class="err">应用的策略映射。</span>
</span></span><span class="line"><span class="cl">     <span class="o">*</span>
</span></span><span class="line"><span class="cl">     <span class="o">*</span> <span class="err">@</span><span class="k">var</span> <span class="n">array</span>
</span></span><span class="line"><span class="cl">     <span class="o">*/</span>
</span></span><span class="line"><span class="cl">    <span class="n">protected</span> <span class="o">$</span><span class="n">policies</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="n">Article</span><span class="p">::</span><span class="k">class</span> <span class="o">=&gt;</span> <span class="n">ArticlePolicy</span><span class="p">::</span><span class="k">class</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="o">/**</span>
</span></span><span class="line"><span class="cl">     <span class="o">*</span> <span class="err">注册任意用户认证、用户授权服务。</span>
</span></span><span class="line"><span class="cl">     <span class="o">*</span>
</span></span><span class="line"><span class="cl">     <span class="o">*</span> <span class="err">@</span><span class="k">return</span> <span class="n">void</span>
</span></span><span class="line"><span class="cl">     <span class="o">*/</span>
</span></span><span class="line"><span class="cl">    <span class="n">public</span> <span class="n">function</span> <span class="n">boot</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">$</span><span class="n">this</span><span class="o">-&gt;</span><span class="n">registerPolicies</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="o">//</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="编写-policy-校验逻辑">编写 Policy 校验逻辑</h2>
<p>接下来就在 <code>ArticlePolicy</code> 策略中编写校验用户是否拥有修改文章的权限的方法。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">&lt;?php
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">namespace App\Policies;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">use App\Models\Article;
</span></span><span class="line"><span class="cl">use App\User;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">class ArticlePolicy
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    public function update(User $user, Article $article)
</span></span><span class="line"><span class="cl">    {
</span></span><span class="line"><span class="cl">        return $user-&gt;id == $article-&gt;user_id;
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>在 <code>update</code> 方法中判断文章作者id是否等于当前登录的用户id，返回 true 或 false。true 可以进行修改操作，false 则会抛出没有权限。</p>
<h2 id="使用-policy">使用 Policy</h2>
<p>然后就是在控制器中的使用了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="o">&lt;</span><span class="err">?</span><span class="n">php</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">namespace</span> <span class="n">App</span>\<span class="n">Http</span>\<span class="n">Controllers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">App</span>\<span class="n">Models</span>\<span class="n">Article</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">use</span> <span class="n">Illuminate</span>\<span class="n">Http</span>\<span class="n">Request</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="n">ArticleController</span> <span class="k">extends</span> <span class="n">Controller</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">public</span> <span class="n">function</span> <span class="n">edit</span><span class="p">(</span><span class="n">Request</span> <span class="o">$</span><span class="n">request</span><span class="p">,</span> <span class="n">Article</span> <span class="o">$</span><span class="n">article</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="o">//</span> <span class="err">校验用户是否有操作权限</span>
</span></span><span class="line"><span class="cl">        <span class="o">$</span><span class="n">this</span><span class="o">-&gt;</span><span class="n">authorize</span><span class="p">(</span><span class="s1">&#39;update&#39;</span><span class="p">,</span> <span class="o">$</span><span class="n">article</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="o">//</span> <span class="err">更新文章</span>
</span></span><span class="line"><span class="cl">        <span class="o">$</span><span class="n">article</span><span class="o">-&gt;</span><span class="n">fill</span><span class="p">(</span><span class="o">$</span><span class="n">request</span><span class="o">-&gt;</span><span class="n">all</span><span class="p">());</span>
</span></span><span class="line"><span class="cl">        <span class="o">$</span><span class="n">article</span><span class="o">-&gt;</span><span class="n">save</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>authorize</code> 的第一个参数表示本次验证使用  <code>ArticlePolicy</code>  里面的 <code>update</code> 方法， <code>$article</code> 实例用来判断使用哪一种策略，当然也可以不用指定具体实例，只需要传递一个类名。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">$this-&gt;authorize(&#39;update&#39;, Article::class);
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>Laravel 应用部署到 Nginx 服务器上的第一个坑</title>
      <link>https://her-cat.com/posts/2017/08/24/laravel-deploy-nginx-problem/</link>
      <pubDate>Thu, 24 Aug 2017 20:33:51 +0800</pubDate>
      <guid>https://her-cat.com/posts/2017/08/24/laravel-deploy-nginx-problem/</guid>
      <description>前言在学习了一段时间 Laravel 后，写了个生成短网址小应用，把应用部署到服务器上的时候就出现了问题.....</description>
      <content:encoded><![CDATA[<h3 id="前言">前言</h3>
<p>在学习了一段时间 Larvel 后，写了个生成短网址小应用，把应用部署到服务器上的时候就出现了问题&hellip;..</p>
<p>以此文祭奠我失去的青春&hellip;</p>
<h3 id="正文">正文</h3>
<p>在服务器上配置完成后，打开浏览器访问域名，返回该地址无法响应请求，什么错误信息都没有。于是建了一个 PHP 文件，访问后正常运行，说明环境是没问题的。经过搜索，有人说要给 storage 权限，执行命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sudo chmod -R 777 storage/
</span></span></code></pre></div><p>还是不能运行，又看到文章说先要让 PHP 显示错误信息。首先要找到你的 php.ini 文件，可以通过 whereis php.ini 来进行搜索，我的路径是 /usr/local/php/etc/php.ini，然后将 display_errors = Off 改成 On。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">cd /usr/local/php/etc
</span></span><span class="line"><span class="cl">vim php.ini
</span></span></code></pre></div><p>保存以后重启 Nginx 服务：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">service nginx restart
</span></span></code></pre></div><p>刷新页面就可以看到报错信息：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">Warning</span><span class="p">:</span> <span class="n">require</span><span class="p">():</span> <span class="n">open_basedir</span> <span class="n">restriction</span> <span class="ow">in</span> <span class="n">effect</span><span class="o">.</span> <span class="ne">File</span><span class="p">(</span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">www</span><span class="o">/</span><span class="n">ShortUrl</span><span class="o">/</span><span class="n">bootstrap</span><span class="o">/</span><span class="n">autoload</span><span class="o">.</span><span class="n">php</span><span class="p">)</span> <span class="n">is</span> <span class="ow">not</span> <span class="n">within</span> <span class="n">the</span> <span class="n">allowed</span> <span class="n">path</span><span class="p">(</span><span class="n">s</span><span class="p">):</span> <span class="p">(</span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">www</span><span class="o">/</span><span class="n">ShortUrl</span><span class="o">/</span><span class="n">public</span><span class="o">/</span><span class="p">:</span><span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="p">:</span><span class="o">/</span><span class="n">proc</span><span class="o">/</span><span class="p">)</span> <span class="ow">in</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">www</span><span class="o">/</span><span class="n">ShortUrl</span><span class="o">/</span><span class="n">public</span><span class="o">/</span><span class="n">index</span><span class="o">.</span><span class="n">php</span> <span class="n">on</span> <span class="n">line</span> <span class="mi">22</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Warning</span><span class="p">:</span> <span class="n">require</span><span class="p">(</span><span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">www</span><span class="o">/</span><span class="n">ShortUrl</span><span class="o">/</span><span class="n">bootstrap</span><span class="o">/</span><span class="n">autoload</span><span class="o">.</span><span class="n">php</span><span class="p">):</span> <span class="n">failed</span> <span class="n">to</span> <span class="n">open</span> <span class="n">stream</span><span class="p">:</span> <span class="n">Operation</span> <span class="ow">not</span> <span class="n">permitted</span> <span class="ow">in</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">www</span><span class="o">/</span><span class="n">ShortUrl</span><span class="o">/</span><span class="n">public</span><span class="o">/</span><span class="n">index</span><span class="o">.</span><span class="n">php</span> <span class="n">on</span> <span class="n">line</span> <span class="mi">22</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">Fatal</span> <span class="n">error</span><span class="p">:</span> <span class="n">require</span><span class="p">():</span> <span class="n">Failed</span> <span class="n">opening</span> <span class="n">required</span> <span class="s1">&#39;/home/www/ShortUrl/public/../bootstrap/autoload.php&#39;</span> <span class="p">(</span><span class="n">include_path</span><span class="o">=</span><span class="s1">&#39;.:/usr/local/php/lib/php&#39;</span><span class="p">)</span> <span class="ow">in</span> <span class="o">/</span><span class="n">home</span><span class="o">/</span><span class="n">www</span><span class="o">/</span><span class="n">ShortUrl</span><span class="o">/</span><span class="n">public</span><span class="o">/</span><span class="n">index</span><span class="o">.</span><span class="n">php</span> <span class="n">on</span> <span class="n">line</span> <span class="mi">22</span>
</span></span></code></pre></div><p>open_basedir 可以将用户访问文件的活动范围限制在指定的区域，而上面的错误的意思就是 /home/www/ShortUrl/bootstrap/autoload.php 不在允许的路径：/home/www/ShortUrl/public/:/tmp/:/proc/里面。</p>
<p>因为 Laravel 入口文件在 public 目录下面，所以默认访问文件的活动范围只能在 public 目录。但是 Laravel 的代码目录都与 public 同级，也就是在 public 外面，当 index.php 请求代码目录的时候，就会抛出异常。</p>
<p>解决办法就是设置 open_basedir 的值，改变用户访问文件的活动范围。依旧是打开 php.ini 文件，在文件末尾加上一下内容：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[HOST=example.com]</span>
</span></span><span class="line"><span class="cl"><span class="na">open_basedir</span><span class="o">=</span><span class="s">/home/www/ShortUrl/:/tmp/</span>
</span></span><span class="line"><span class="cl"><span class="k">[PATH=/home/www/ShortUrl/public]</span>
</span></span><span class="line"><span class="cl"><span class="na">open_basedir</span><span class="o">=</span><span class="s">/home/www/ShortUrl/:/tmp/</span>
</span></span></code></pre></div><p>HOST 就是 Laravel 应用的域名，PATH 就是应用的入口目录。刷新页面，Laravel 就可以正常运行了。</p>
<blockquote>
<p>这是一篇过去很久的文章，其中的信息可能已经有所发展或是发生改变。</p></blockquote>
]]></content:encoded>
    </item><follow_challenge>
      <feedId>58021783493571598</feedId>
      <userId>56882619875632128</userId>
    </follow_challenge>
  </channel>
</rss>
